//////////////////////////////////////////////////////////////////// // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine // All rights reserved // This file was released under the GPLv2 on June 2015. //////////////////////////////////////////////////////////////////// /*********************************************************************/ OSSTATUS __fastcall WCacheCheckLimits(IN PW_CACHE Cache, IN PVOID Context, IN lba_t ReqLba, IN ULONG BCount); OSSTATUS __fastcall WCacheCheckLimitsRAM(IN PW_CACHE Cache, IN PVOID Context, IN lba_t ReqLba, IN ULONG BCount); OSSTATUS __fastcall WCacheCheckLimitsRW(IN PW_CACHE Cache, IN PVOID Context, IN lba_t ReqLba, IN ULONG BCount); OSSTATUS __fastcall WCacheCheckLimitsR(IN PW_CACHE Cache, IN PVOID Context, IN lba_t ReqLba, IN ULONG BCount); VOID __fastcall WCachePurgeAllRW(IN PW_CACHE Cache, IN PVOID Context); VOID __fastcall WCacheFlushAllRW(IN PW_CACHE Cache, IN PVOID Context); VOID __fastcall WCachePurgeAllR(IN PW_CACHE Cache, IN PVOID Context); OSSTATUS __fastcall WCacheDecodeFlags(IN PW_CACHE Cache, IN ULONG Flags); #define ASYNC_STATE_NONE 0 #define ASYNC_STATE_READ_PRE 1 #define ASYNC_STATE_READ 2 #define ASYNC_STATE_WRITE_PRE 3 #define ASYNC_STATE_WRITE 4 #define ASYNC_STATE_DONE 5 #define ASYNC_CMD_NONE 0 #define ASYNC_CMD_READ 1 #define ASYNC_CMD_UPDATE 2 #define WCACHE_MAX_CHAIN (0x10) #define MEM_WCCTX_TAG 'xtCW' #define MEM_WCFRM_TAG 'rfCW' #define MEM_WCBUF_TAG 'fbCW' #define USE_WC_PRINT #ifdef USE_WC_PRINT #define WcPrint UDFPrint #else #define WcPrint(x) {;} #endif typedef struct _W_CACHE_ASYNC { UDF_PH_CALL_CONTEXT PhContext; ULONG State; ULONG Cmd; PW_CACHE Cache; PVOID Buffer; PVOID Buffer2; SIZE_T TransferredBytes; ULONG BCount; lba_t Lba; struct _W_CACHE_ASYNC* NextWContext; struct _W_CACHE_ASYNC* PrevWContext; } W_CACHE_ASYNC, *PW_CACHE_ASYNC; VOID WCacheUpdatePacketComplete( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to head async IO context IN OUT PW_CACHE_ASYNC* PrevWContext, // pointer to tail async IO context IN BOOLEAN FreePacket = TRUE ); /*********************************************************************/ ULONG WCache_random; /* WCacheInit__() fills all necesary fileds in passed in PW_CACHE Cache structure, allocates memory and synchronization resources. Cacheable area is subdiveded on Frames - contiguous sets of blocks. Internally each Frame is an array of pointers and attributes of cached Blocks. To optimize memory usage WCache keeps in memory limited number of frames (MaxFrames). Frame length (number of Blocks) must be be a power of 2 and aligned on minimum writeable block size - Packet. Packet size must be a power of 2 (2, 4, 8, 16, etc.). Each cached Block belongs to one of the Frames. To optimize memory usage WCache keeps in memory limited number of Blocks (MaxBlocks). Block size must be a power of 2. WCache splits low-level request(s) into some parts if requested data length exceeds MaxBytesToRead. If requested data length exceeds maximum cache size WCache makes recursive calls to read/write routines with shorter requests WCacheInit__() returns initialization status. If initialization failed, all allocated memory and resources are automaticelly freed. Public routine */ OSSTATUS WCacheInit__( IN PW_CACHE Cache, // pointer to the Cache Control structure to be initialized IN ULONG MaxFrames, // maximum number of Frames to be kept in memory // simultaneously IN ULONG MaxBlocks, // maximum number of Blocks to be kept in memory // simultaneously IN SIZE_T MaxBytesToRead, // maximum IO length (split boundary) IN ULONG PacketSizeSh, // number of blocks in packet (bit shift) // Packes size = 2^PacketSizeSh IN ULONG BlockSizeSh, // Block size (bit shift) // Block size = 2^BlockSizeSh IN ULONG BlocksPerFrameSh,// number of blocks in Frame (bit shift) // Frame size = 2^BlocksPerFrameSh IN lba_t FirstLba, // Logical Block Address (LBA) of the 1st block // in cacheable area IN lba_t LastLba, // Logical Block Address (LBA) of the last block // in cacheable area IN ULONG Mode, // media mode: // WCACHE_MODE_ROM // WCACHE_MODE_RW // WCACHE_MODE_R // WCACHE_MODE_RAM // the following modes are planned to be implemented: // WCACHE_MODE_EWR IN ULONG Flags, // cache mode flags: // WCACHE_CACHE_WHOLE_PACKET // read long (Packet-sized) blocks of // data from media IN ULONG FramesToKeepFree, // number of Frames to be flushed & purged from cache // when Frame counter reaches top-limit and allocation // of a new Frame required IN PWRITE_BLOCK WriteProc, // pointer to synchronous physical write call-back routine IN PREAD_BLOCK ReadProc, // pointer to synchronous physical read call-back routine IN PWRITE_BLOCK_ASYNC WriteProcAsync, // pointer to _asynchronous_ physical write call-back routine // currently must be set to NULL because async support // is not completly implemented IN PREAD_BLOCK_ASYNC ReadProcAsync, // pointer to _asynchronous_ physical read call-back routine // must be set to NULL (see above) IN PCHECK_BLOCK CheckUsedProc, // pointer to call-back routine that checks whether the Block // specified (by LBA) is allocated for some data or should // be treated as unused (and thus, zero-filled). // Is used to avoid physical reads and writes from/to such Blocks IN PUPDATE_RELOC UpdateRelocProc, // pointer to call-back routine that updates caller's // relocation table _after_ physical write (append) in WORM // (WCACHE_MODE_R) mode. WCache sends original and new // (derived from last LBA) logical addresses to this routine IN PWC_ERROR_HANDLER ErrorHandlerProc ) { ULONG l1, l2, l3; ULONG PacketSize = (1) << PacketSizeSh; ULONG BlockSize = (1) << BlockSizeSh; ULONG BlocksPerFrame = (1) << BlocksPerFrameSh; OSSTATUS RC = STATUS_SUCCESS; LARGE_INTEGER rseed; ULONG res_init_flags = 0; #define WCLOCK_RES 1 _SEH2_TRY { // check input parameters if(Mode == WCACHE_MODE_R) { UDFPrint(("Disable Async-Write for WORM media\n")); WriteProcAsync = NULL; } if((MaxBlocks % PacketSize) || !MaxBlocks) { UDFPrint(("Total number of sectors must be packet-size-aligned\n")); try_return(RC = STATUS_INVALID_PARAMETER); } if(BlocksPerFrame % PacketSize) { UDFPrint(("Number of sectors per Frame must be packet-size-aligned\n")); try_return(RC = STATUS_INVALID_PARAMETER); } if(!ReadProc) { UDFPrint(("Read routine pointer must be valid\n")); try_return(RC = STATUS_INVALID_PARAMETER); } if(FirstLba >= LastLba) { UDFPrint(("Invalid cached area parameters: (%x - %x)\n",FirstLba, LastLba)); try_return(RC = STATUS_INVALID_PARAMETER); } if(!MaxFrames) { UDFPrint(("Total frame number must be non-zero\n",FirstLba, LastLba)); try_return(RC = STATUS_INVALID_PARAMETER); } if(Mode > WCACHE_MODE_MAX) { UDFPrint(("Invalid media mode. Should be 0-%x\n",WCACHE_MODE_MAX)); try_return(RC = STATUS_INVALID_PARAMETER); } if(FramesToKeepFree >= MaxFrames/2) { UDFPrint(("Invalid FramesToKeepFree (%x). Should be Less or equal to MaxFrames/2 (%x)\n", FramesToKeepFree, MaxFrames/2)); try_return(RC = STATUS_INVALID_PARAMETER); } // check 'features' if(!WriteProc) { UDFPrint(("Write routine not specified\n")); UDFPrint(("Read-only mode enabled\n")); } MaxBlocks = max(MaxBlocks, BlocksPerFrame*3); // initialize required structures // we'll align structure size on system page size to // avoid system crashes caused by pool fragmentation if(!(Cache->FrameList = (PW_CACHE_FRAME)MyAllocatePoolTag__(NonPagedPool, l1 = (((LastLba >> BlocksPerFrameSh)+1)*sizeof(W_CACHE_FRAME)), MEM_WCFRM_TAG) )) { UDFPrint(("Cache init err 1\n")); try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } if(!(Cache->CachedBlocksList = (PULONG)MyAllocatePoolTag__(NonPagedPool, l2 = ((MaxBlocks+2)*sizeof(lba_t)), MEM_WCFRM_TAG) )) { UDFPrint(("Cache init err 2\n")); try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } if(!(Cache->CachedModifiedBlocksList = (PULONG)MyAllocatePoolTag__(NonPagedPool, l2, MEM_WCFRM_TAG) )) { UDFPrint(("Cache init err 3\n")); try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } if(!(Cache->CachedFramesList = (PULONG)MyAllocatePoolTag__(NonPagedPool, l3 = ((MaxFrames+2)*sizeof(lba_t)), MEM_WCFRM_TAG) )) { UDFPrint(("Cache init err 4\n")); try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } RtlZeroMemory(Cache->FrameList, l1); RtlZeroMemory(Cache->CachedBlocksList, l2); RtlZeroMemory(Cache->CachedModifiedBlocksList, l2); RtlZeroMemory(Cache->CachedFramesList, l3); // remember all useful parameters Cache->BlocksPerFrame = BlocksPerFrame; Cache->BlocksPerFrameSh = BlocksPerFrameSh; Cache->BlockCount = 0; Cache->MaxBlocks = MaxBlocks; Cache->MaxBytesToRead = MaxBytesToRead; Cache->FrameCount = 0; Cache->MaxFrames = MaxFrames; Cache->PacketSize = PacketSize; Cache->PacketSizeSh = PacketSizeSh; Cache->BlockSize = BlockSize; Cache->BlockSizeSh = BlockSizeSh; Cache->WriteCount = 0; Cache->FirstLba = FirstLba; Cache->LastLba = LastLba; Cache->Mode = Mode; if(!OS_SUCCESS(RC = WCacheDecodeFlags(Cache, Flags))) { return RC; } Cache->FramesToKeepFree = FramesToKeepFree; Cache->WriteProc = WriteProc; Cache->ReadProc = ReadProc; Cache->WriteProcAsync = WriteProcAsync; Cache->ReadProcAsync = ReadProcAsync; Cache->CheckUsedProc = CheckUsedProc; Cache->UpdateRelocProc = UpdateRelocProc; Cache->ErrorHandlerProc = ErrorHandlerProc; // init permanent tmp buffers if(!(Cache->tmp_buff = (PCHAR)MyAllocatePoolTag__(NonPagedPool, PacketSize*BlockSize, MEM_WCFRM_TAG))) { UDFPrint(("Cache init err 5.W\n")); try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } if(!(Cache->tmp_buff_r = (PCHAR)MyAllocatePoolTag__(NonPagedPool, PacketSize*BlockSize, MEM_WCFRM_TAG))) { UDFPrint(("Cache init err 5.R\n")); try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } if(!(Cache->reloc_tab = (PULONG)MyAllocatePoolTag__(NonPagedPool, Cache->PacketSize*sizeof(ULONG), MEM_WCFRM_TAG))) { UDFPrint(("Cache init err 6\n")); try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } if(!OS_SUCCESS(RC = ExInitializeResourceLite(&(Cache->WCacheLock)))) { UDFPrint(("Cache init err (res)\n")); try_return(RC); } res_init_flags |= WCLOCK_RES; KeQuerySystemTime((PLARGE_INTEGER)(&rseed)); WCache_random = rseed.LowPart; try_exit: NOTHING; } _SEH2_FINALLY { if(!OS_SUCCESS(RC)) { if(res_init_flags & WCLOCK_RES) ExDeleteResourceLite(&(Cache->WCacheLock)); if(Cache->FrameList) MyFreePool__(Cache->FrameList); if(Cache->CachedBlocksList) MyFreePool__(Cache->CachedBlocksList); if(Cache->CachedModifiedBlocksList) MyFreePool__(Cache->CachedModifiedBlocksList); if(Cache->CachedFramesList) MyFreePool__(Cache->CachedFramesList); if(Cache->tmp_buff_r) MyFreePool__(Cache->tmp_buff_r); if(Cache->tmp_buff) MyFreePool__(Cache->tmp_buff); if(Cache->reloc_tab) MyFreePool__(Cache->reloc_tab); RtlZeroMemory(Cache, sizeof(W_CACHE)); } else { Cache->Tag = 0xCAC11E00; } } _SEH2_END; return RC; } // end WCacheInit__() /* WCacheRandom() - just a random generator Returns random LONGLONG number Internal routine */ LONGLONG WCacheRandom(VOID) { WCache_random = (WCache_random * 0x8088405 + 1); return WCache_random; } // end WCacheRandom() /* WCacheFindLbaToRelease() finds Block to be flushed and purged from cache Returns random LBA Internal routine */ lba_t __fastcall WCacheFindLbaToRelease( IN PW_CACHE Cache ) { if(!(Cache->BlockCount)) return WCACHE_INVALID_LBA; return(Cache->CachedBlocksList[((ULONG)WCacheRandom() % Cache->BlockCount)]); } // end WCacheFindLbaToRelease() /* WCacheFindModifiedLbaToRelease() finds Block to be flushed and purged from cache. This routine looks for Blocks among modified ones Returns random LBA (nodified) Internal routine */ lba_t __fastcall WCacheFindModifiedLbaToRelease( IN PW_CACHE Cache ) { if(!(Cache->WriteCount)) return WCACHE_INVALID_LBA; return(Cache->CachedModifiedBlocksList[((ULONG)WCacheRandom() % Cache->WriteCount)]); } // end WCacheFindModifiedLbaToRelease() /* WCacheFindFrameToRelease() finds Frame to be flushed and purged with all Blocks (from this Frame) from cache Returns random Frame number Internal routine */ lba_t __fastcall WCacheFindFrameToRelease( IN PW_CACHE Cache ) { ULONG i, j; ULONG frame = 0; ULONG prev_uc = -1; ULONG uc = -1; lba_t lba; BOOLEAN mod = FALSE; if(!(Cache->FrameCount)) return 0; /* return(Cache->CachedFramesList[((ULONG)WCacheRandom() % Cache->FrameCount)]); */ for(i=0; iFrameCount; i++) { j = Cache->CachedFramesList[i]; mod |= (Cache->FrameList[j].UpdateCount != 0); uc = Cache->FrameList[j].UpdateCount*32 + Cache->FrameList[j].AccessCount; if(prev_uc > uc) { prev_uc = uc; frame = j; } } if(!mod) { frame = Cache->CachedFramesList[((ULONG)WCacheRandom() % Cache->FrameCount)]; lba = frame << Cache->BlocksPerFrameSh; WcPrint(("WC:-frm %x\n", lba)); } else { lba = frame << Cache->BlocksPerFrameSh; WcPrint(("WC:-frm(mod) %x\n", lba)); for(i=0; iFrameCount; i++) { j = Cache->CachedFramesList[i]; Cache->FrameList[j].UpdateCount = (Cache->FrameList[j].UpdateCount*2)/3; Cache->FrameList[j].AccessCount = (Cache->FrameList[j].AccessCount*3)/4; } } return frame; } // end WCacheFindFrameToRelease() /* WCacheGetSortedListIndex() returns index of searched Lba (Lba is ULONG in sorted array) or index of minimal cached Lba greater than searched. If requested Lba is less than minimum cached, 0 is returned. If requested Lba is greater than maximum cached, BlockCount value is returned. Internal routine */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4035) // re-enable below #endif ULONG //__fastcall WCacheGetSortedListIndex( IN ULONG BlockCount, // number of items in array (pointed by List) IN lba_t* List, // pointer to sorted (ASC) array of ULONGs IN lba_t Lba // ULONG value to be searched for ) { if(!BlockCount) return 0; #if defined(_X86_) && defined(_MSC_VER) && !defined(__clang__) __asm push ecx __asm push ebx __asm push edx __asm push esi __asm push edi // left = 0; // right = BlockCount - 1; // pos = 0; __asm xor edx,edx // left __asm mov ebx,BlockCount __asm dec ebx // right __asm xor esi,esi // pos __asm mov edi,List // List __asm mov ecx,Lba // Lba While_1: // while(left != right) { __asm cmp edx,ebx __asm jz EO_while_1 // pos = (left + right) >> 1; __asm lea esi,[ebx+edx] __asm shr esi,1 // if(List[pos] == Lba) // return pos; __asm mov eax,[edi+esi*4] __asm cmp eax,ecx __asm jz EO_while_2 // if(right - left == 1) { __asm sub ebx,edx __asm cmp ebx,1 __asm jne NO_r_sub_l_eq_1 // if(List[pos+1] < Lba) <=> if(List[pos+1] >= Lba) // return (pos+2); <=> break; // break; <=> return (pos+2); __asm cmp [edi+esi*4+4],ecx __asm jae EO_while_1 __asm add esi,2 __asm jmp EO_while_2 // } NO_r_sub_l_eq_1: // if(List[pos] < Lba) { __asm cmp eax,ecx __asm jae Update_r // left = pos; __asm add ebx,edx __asm mov edx,esi __asm jmp While_1 // } else { Update_r: // right = pos; __asm mov ebx,esi __asm jmp While_1 // } // } EO_while_1: // if((List[pos] < Lba) && ((pos+1) <= BlockCount)) pos++; __asm mov eax,[edi+esi*4] __asm cmp eax,ecx __asm jae EO_while_2 __asm inc esi __asm cmp esi,BlockCount __asm jbe EO_while_2 __asm dec esi EO_while_2: // return pos; __asm mov eax,esi __asm pop edi __asm pop esi __asm pop edx __asm pop ebx __asm pop ecx #else // NO X86 optimization , use generic C/C++ ULONG pos; ULONG left; ULONG right; if(!BlockCount) return 0; left = 0; right = BlockCount - 1; pos = 0; while(left != right) { pos = (left + right) >> 1; if(List[pos] == Lba) return pos; if(right - left == 1) { if(List[pos+1] < Lba) return (pos+2); break; } if(List[pos] < Lba) { left = pos; } else { right = pos; } } if((List[pos] < Lba) && ((pos+1) <= BlockCount)) pos++; return pos; #endif // _X86_ } #ifdef _MSC_VER #pragma warning(pop) // re-enable warning #4035 #endif /* WCacheInsertRangeToList() inserts values laying in range described by Lba (1st value) and BCount (number of sequentially incremented values) in sorted array of ULONGs pointed by List. Ex.: (Lba, BCount)=(7,3) will insert values {7,8,9}. If target array already contains one or more values falling in requested range, they will be removed before insertion. WCacheInsertRangeToList() updates value of (*BlockCount) to reflect performed changes. WCacheInsertRangeToList() assumes that target array is of enough size. Internal routine */ VOID __fastcall WCacheInsertRangeToList( IN lba_t* List, // pointer to sorted (ASC) array of ULONGs IN PULONG BlockCount, // pointer to number of items in array (pointed by List) IN lba_t Lba, // initial value for insertion IN ULONG BCount // number of sequentially incremented values to be inserted ) { if(!BCount) return; ASSERT(!(BCount & 0x80000000)); ULONG firstPos = WCacheGetSortedListIndex(*BlockCount, List, Lba); ULONG lastPos = WCacheGetSortedListIndex(*BlockCount, List, Lba+BCount); ULONG offs = firstPos + BCount - lastPos; if(offs) { // move list tail // ASSERT(lastPos+offs + ((*BlockCount) - lastPos) <= qq); if(*BlockCount) { #ifdef WCACHE_BOUND_CHECKS MyCheckArray(List, lastPos+offs+(*BlockCount)-lastPos-1); #endif //WCACHE_BOUND_CHECKS DbgMoveMemory(&(List[lastPos+offs]), &(List[lastPos]), ((*BlockCount) - lastPos) * sizeof(ULONG)); } lastPos += offs; for(; firstPosFrameCount; #endif //DBG // We are about to add new cache frame. // Thus check if we have enough free entries and // flush unused ones if it is neccessary. if(Cache->FrameCount >= Cache->MaxFrames) { BrutePoint(); WCacheCheckLimits(Cache, Context, frame << Cache->BlocksPerFrameSh, Cache->PacketSize*2); } ASSERT(Cache->FrameCount < Cache->MaxFrames); block_array = (PW_CACHE_ENTRY)MyAllocatePoolTag__(NonPagedPool, l = sizeof(W_CACHE_ENTRY) << Cache->BlocksPerFrameSh, MEM_WCFRM_TAG); Cache->FrameList[frame].Frame = block_array; // Keep history !!! //Cache->FrameList[frame].UpdateCount = 0; //Cache->FrameList[frame].AccessCount = 0; if(block_array) { ASSERT((ULONG_PTR)block_array > 0x1000); WCacheInsertItemToList(Cache->CachedFramesList, &(Cache->FrameCount), frame); RtlZeroMemory(block_array, l); } else { BrutePoint(); } ASSERT(Cache->FrameCount <= Cache->MaxFrames); #ifdef DBG ASSERT(old_count < Cache->FrameCount); #endif //DBG return block_array; } // end WCacheInitFrame() /* WCacheRemoveFrame() frees storage for Frame (block_array) with index 'frame' and removes it's index from sorted array of frame indexes. Internal routine */ VOID __fastcall WCacheRemoveFrame( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user's context (currently unused) IN ULONG frame // frame index ) { PW_CACHE_ENTRY block_array; #ifdef DBG ULONG old_count = Cache->FrameCount; #endif //DBG ASSERT(Cache->FrameCount <= Cache->MaxFrames); block_array = Cache->FrameList[frame].Frame; WCacheRemoveItemFromList(Cache->CachedFramesList, &(Cache->FrameCount), frame); MyFreePool__(block_array); // ASSERT(!(Cache->FrameList[frame].WriteCount)); // ASSERT(!(Cache->FrameList[frame].WriteCount)); Cache->FrameList[frame].Frame = NULL; ASSERT(Cache->FrameCount < Cache->MaxFrames); #ifdef DBG ASSERT(old_count > Cache->FrameCount); #endif //DBG } // end WCacheRemoveFrame() /* WCacheSetModFlag() sets Modified flag for Block with offset 'i' in Frame 'block_array' Internal routine */ #define WCacheSetModFlag(block_array, i) \ *((PULONG)&(block_array[i].Sector)) |= WCACHE_FLAG_MODIFIED /* WCacheClrModFlag() clears Modified flag for Block with offset 'i' in Frame 'block_array' Internal routine */ #define WCacheClrModFlag(block_array, i) \ *((PULONG)&(block_array[i].Sector)) &= ~WCACHE_FLAG_MODIFIED /* WCacheGetModFlag() returns non-zero value if Modified flag for Block with offset 'i' in Frame 'block_array' is set. Otherwise 0 is returned. Internal routine */ #define WCacheGetModFlag(block_array, i) \ (*((PULONG)&(block_array[i].Sector)) & WCACHE_FLAG_MODIFIED) #if 0 /* WCacheSetBadFlag() sets Modified flag for Block with offset 'i' in Frame 'block_array' Internal routine */ #define WCacheSetBadFlag(block_array, i) \ *((PULONG)&(block_array[i].Sector)) |= WCACHE_FLAG_BAD /* WCacheClrBadFlag() clears Modified flag for Block with offset 'i' in Frame 'block_array' Internal routine */ #define WCacheClrBadFlag(block_array, i) \ *((PULONG)&(block_array[i].Sector)) &= ~WCACHE_FLAG_BAD /* WCacheGetBadFlag() returns non-zero value if Modified flag for Block with offset 'i' in Frame 'block_array' is set. Otherwise 0 is returned. Internal routine */ #define WCacheGetBadFlag(block_array, i) \ (((UCHAR)(block_array[i].Sector)) & WCACHE_FLAG_BAD) #endif //0 /* WCacheSectorAddr() returns pointer to memory block containing cached data for Block described by Frame (block_array) and offset in this Frame (i). If requested Block is not cached yet NULL is returned. Internal routine */ #define WCacheSectorAddr(block_array, i) \ ((ULONG_PTR)(block_array[i].Sector) & WCACHE_ADDR_MASK) /* WCacheFreeSector() releases memory block containing cached data for Block described by Frame (block_array) and offset in this Frame (i). Should never be called for non-cached Blocks. Internal routine */ #define WCacheFreeSector(frame, offs) \ { \ DbgFreePool((PVOID)WCacheSectorAddr(block_array, offs)); \ block_array[offs].Sector = NULL; \ Cache->FrameList[frame].BlockCount--; \ } /* WCacheAllocAsyncEntry() allocates storage for async IO context, links it to previously allocated async IO context (if any), initializes synchronization (completion) event and allocates temporary IO buffers. Async IO contexts are used to create chained set of IO requests durring top-level request precessing and wait for their completion. Internal routine */ PW_CACHE_ASYNC WCacheAllocAsyncEntry( IN PW_CACHE Cache, // pointer to the Cache Control structure IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to the pointer to // the head of async IO context chain IN OUT PW_CACHE_ASYNC* PrevWContext, // pointer to the storage for pointer // to newly allocated async IO context chain IN ULONG BufferSize // requested IO buffer size ) { PW_CACHE_ASYNC WContext; PCHAR Buffer; WContext = (PW_CACHE_ASYNC)MyAllocatePoolTag__(NonPagedPool,sizeof(W_CACHE_ASYNC), MEM_WCCTX_TAG); if(!WContext) return NULL; Buffer = (PCHAR)DbgAllocatePoolWithTag(NonPagedPool, BufferSize*(2-Cache->Chained), MEM_WCBUF_TAG); if(!Buffer) { MyFreePool__(WContext); return NULL; } if(!Cache->Chained) KeInitializeEvent(&(WContext->PhContext.event), SynchronizationEvent, FALSE); WContext->Cache = Cache; if(*PrevWContext) (*PrevWContext)->NextWContext = WContext; // WContext->NextWContext = (*PrevWContext); WContext->NextWContext = NULL; WContext->Buffer = Buffer; WContext->Buffer2 = Buffer+(Cache->Chained ? 0 : BufferSize); if(!(*FirstWContext)) (*FirstWContext) = WContext; (*PrevWContext) = WContext; return WContext; } // end WCacheAllocAsyncEntry() /* WCacheFreeAsyncEntry() releases storage previously allocated for async IO context. Internal routine */ VOID WCacheFreeAsyncEntry( IN PW_CACHE Cache, // pointer to the Cache Control structure PW_CACHE_ASYNC WContext // pointer to async IO context to release ) { DbgFreePool(WContext->Buffer); MyFreePool__(WContext); } // end WCacheFreeAsyncEntry() //#define WCacheRaiseIoError(c, ct, s, l, bc, b, o, r) OSSTATUS WCacheRaiseIoError( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, IN OSSTATUS Status, IN ULONG Lba, IN ULONG BCount, IN PVOID Buffer, IN BOOLEAN ReadOp, IN PBOOLEAN Retry ) { if(!Cache->ErrorHandlerProc) return Status; WCACHE_ERROR_CONTEXT ec; ec.WCErrorCode = ReadOp ? WCACHE_ERROR_READ : WCACHE_ERROR_WRITE; ec.Status = Status; ec.ReadWrite.Lba = Lba; ec.ReadWrite.BCount = BCount; ec.ReadWrite.Buffer = Buffer; Status = Cache->ErrorHandlerProc(Context, &ec); if(Retry) (*Retry) = ec.Retry; return Status; } // end WCacheRaiseIoError() /* WCacheUpdatePacket() attempts to updates packet containing target Block. If async IO is enabled new IO context is added to the chain. If packet containing target Block is modified and PrefereWrite flag is NOT set, function returns with status STATUS_RETRY. This setting is user in WCACHE_MODE_R mode to reduce physical writes on flush. 'State' parameter is used in async mode to determine the next processing stege for given request Internal routine */ OSSTATUS WCacheUpdatePacket( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user's context to be passed to user-supplied // low-level IO routines (IO callbacks) IN OUT PW_CACHE_ASYNC* FirstWContext, // pointer to head async IO context IN OUT PW_CACHE_ASYNC* PrevWContext, // pointer to tail async IO context IN PW_CACHE_ENTRY block_array, // pointer to target Frame IN lba_t firstLba, // LBA of the 1st block in target Frame IN lba_t Lba, // LBA of target Block IN ULONG BSh, // bit shift for Block size IN ULONG BS, // Block size (bytes) IN ULONG PS, // Packet size (bytes) IN ULONG PSs, // Packet size (sectors) IN PSIZE_T ReadBytes, // pointer to number of successfully read/written bytes IN BOOLEAN PrefereWrite, // allow physical write (flush) of modified packet IN ULONG State // callers state ) { OSSTATUS status; PCHAR tmp_buff = Cache->tmp_buff; PCHAR tmp_buff2 = Cache->tmp_buff; BOOLEAN mod; BOOLEAN read; BOOLEAN zero; ULONG i; lba_t Lba0; PW_CACHE_ASYNC WContext; BOOLEAN Async = (Cache->ReadProcAsync && Cache->WriteProcAsync); ULONG block_type; BOOLEAN Chained = Cache->Chained; // Check if we are going to write down to disk // all prewiously prepared (chained) data if(State == ASYNC_STATE_WRITE) { WContext = (*PrevWContext); tmp_buff = (PCHAR)(WContext->Buffer); tmp_buff2 = (PCHAR)(WContext->Buffer2); if(!Chained) mod = (DbgCompareMemory(tmp_buff2, tmp_buff, PS) != PS); goto try_write; } // Check if packet contains modified blocks // If packet contains non-cached and unchanged, but used // blocks, it must be read from media before modification mod = read = zero = FALSE; Lba0 = Lba - firstLba; for(i=0; iCheckUsedProc(Context, Lba+i)) & WCACHE_BLOCK_USED) ) { // if(block_type & WCACHE_BLOCK_ZERO) { zero = TRUE; } else { read = TRUE; } } } // check if we are allowed to write to media if(mod && !PrefereWrite) { return STATUS_RETRY; } // return STATUS_SUCCESS if requested packet contains no modified blocks if(!mod) { (*ReadBytes) = PS; return STATUS_SUCCESS; } // pefrorm full update cycle: prepare(optional)/read/modify/write // do some preparations if(Chained || Async) { // For chained and async I/O we allocates context entry // and add it to list (chain) // We shall only read data to temporary buffer and // modify it. Write operations will be invoked later. // This is introduced in order to avoid frequent // read.write mode switching, because it significantly degrades // performance WContext = WCacheAllocAsyncEntry(Cache, FirstWContext, PrevWContext, PS); if(!WContext) { //return STATUS_INSUFFICIENT_RESOURCES; // try to recover Chained = FALSE; Async = FALSE; } else { tmp_buff = tmp_buff2 = (PCHAR)(WContext->Buffer); WContext->Lba = Lba; WContext->Cmd = ASYNC_CMD_UPDATE; WContext->State = ASYNC_STATE_NONE; } } // read packet (if it necessary) if(read) { if(Async) { WContext->State = ASYNC_STATE_READ; status = Cache->ReadProcAsync(Context, WContext, tmp_buff, PS, Lba, &(WContext->TransferredBytes)); // tmp_buff2 = (PCHAR)(WContext->Buffer2); (*ReadBytes) = PS; return status; } else { status = Cache->ReadProc(Context, tmp_buff, PS, Lba, ReadBytes, PH_TMP_BUFFER); } if(!OS_SUCCESS(status)) { status = WCacheRaiseIoError(Cache, Context, status, Lba, PSs, tmp_buff, WCACHE_R_OP, NULL); if(!OS_SUCCESS(status)) { return status; } } } else if(zero) { RtlZeroMemory(tmp_buff, PS); } if(Chained) { // indicate that we prepared for writing block to disk WContext->State = ASYNC_STATE_WRITE_PRE; tmp_buff2 = tmp_buff; status = STATUS_SUCCESS; } // modify packet // If we didn't read packet from media, we can't // perform comparison to assure that packet was really modified. // Thus, assume that it is modified in this case. mod = !read || Cache->DoNotCompare; Lba0 = Lba - firstLba; for(i=0; iState == ASYNC_STATE_WRITE_PRE) { // Return if block is prepared for write and we are in chained mode. if(!mod) { // Mark block as written if we have found that data in it // is not actually modified. WContext->State = ASYNC_STATE_DONE; (*ReadBytes) = PS; } return STATUS_SUCCESS; } // write packet // If the check above reported some changes in packet // we should write packet out to media. // Otherwise, just complete request. if(mod) { try_write: if(Async) { WContext->State = ASYNC_STATE_WRITE; status = Cache->WriteProcAsync(Context, WContext, tmp_buff2, PS, Lba, &(WContext->TransferredBytes), FALSE); (*ReadBytes) = PS; } else { status = Cache->WriteProc(Context, tmp_buff2, PS, Lba, ReadBytes, 0); if(!OS_SUCCESS(status)) { status = WCacheRaiseIoError(Cache, Context, status, Lba, PSs, tmp_buff2, WCACHE_W_OP, NULL); } } } else { if(Async) WCacheCompleteAsync__(WContext, STATUS_SUCCESS); (*ReadBytes) = PS; return STATUS_SUCCESS; } return status; } // end WCacheUpdatePacket() /* WCacheFreePacket() releases storage for all Blocks in packet. 'frame' describes Frame, offset - Block in Frame. offset should be aligned on Packet size. Internal routine */ VOID WCacheFreePacket( IN PW_CACHE Cache, // pointer to the Cache Control structure // IN PVOID Context, IN ULONG frame, // Frame index IN PW_CACHE_ENTRY block_array, // Frame IN ULONG offs, // offset in Frame IN ULONG PSs // Packet size (in Blocks) ) { ULONG i; // mark as non-cached & free pool for(i=0; iBlockSize << Cache->PacketSizeSh; // packet size (bytes) ULONG PSs = Cache->PacketSize; ULONG frame; lba_t firstLba; // Walk through all chained blocks and wait // for completion of read operations. // Also invoke writes of already prepared packets. while(WContext) { if(WContext->Cmd == ASYNC_CMD_UPDATE && WContext->State == ASYNC_STATE_READ) { // wait for async read for update DbgWaitForSingleObject(&(WContext->PhContext.event), NULL); WContext->State = ASYNC_STATE_WRITE; WCacheUpdatePacket(Cache, Context, NULL, &WContext, NULL, -1, WContext->Lba, -1, -1, PS, -1, &(WContext->TransferredBytes), TRUE, ASYNC_STATE_WRITE); } else if(WContext->Cmd == ASYNC_CMD_UPDATE && WContext->State == ASYNC_STATE_WRITE_PRE) { // invoke physical write it the packet is prepared for writing // by previuous call to WCacheUpdatePacket() WContext->State = ASYNC_STATE_WRITE; WCacheUpdatePacket(Cache, Context, NULL, &WContext, NULL, -1, WContext->Lba, -1, -1, PS, -1, &(WContext->TransferredBytes), TRUE, ASYNC_STATE_WRITE); WContext->State = ASYNC_STATE_DONE; } else if(WContext->Cmd == ASYNC_CMD_READ && WContext->State == ASYNC_STATE_READ) { // wait for async read DbgWaitForSingleObject(&(WContext->PhContext.event), NULL); } WContext = WContext->NextWContext; } // Walk through all chained blocks and wait // and wait for completion of async writes (if any). // Also free temporary buffers containing already written blocks. WContext = (*FirstWContext); while(WContext) { NextWContext = WContext->NextWContext; if(WContext->Cmd == ASYNC_CMD_UPDATE && WContext->State == ASYNC_STATE_WRITE) { if(!Cache->Chained) DbgWaitForSingleObject(&(WContext->PhContext.event), NULL); frame = WContext->Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; if(FreePacket) { WCacheFreePacket(Cache, frame, Cache->FrameList[frame].Frame, WContext->Lba - firstLba, PSs); } } WCacheFreeAsyncEntry(Cache, WContext); WContext = NextWContext; } (*FirstWContext) = NULL; (*PrevWContext) = NULL; } // end WCacheUpdatePacketComplete() /* WCacheCheckLimits() checks if we've enough free Frame- & Block-entries under Frame- and Block-limit to feet requested Blocks. If there is not enough entries, WCache initiates flush & purge process to satisfy request. This is dispatch routine, which calls WCacheCheckLimitsR() or WCacheCheckLimitsRW() depending on media type. Internal routine */ OSSTATUS __fastcall WCacheCheckLimits( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN lba_t ReqLba, // first LBA to access/cache IN ULONG BCount // number of Blocks to access/cache ) { /* if(!Cache->FrameCount || !Cache->BlockCount) { ASSERT(!Cache->FrameCount); ASSERT(!Cache->BlockCount); if(!Cache->FrameCount) return STATUS_SUCCESS; }*/ // check if we have reached Frame or Block limit if(!Cache->FrameCount && !Cache->BlockCount) { return STATUS_SUCCESS; } // check for empty frames if(Cache->FrameCount > (Cache->MaxFrames*3)/4) { ULONG frame; ULONG i; for(i=Cache->FrameCount; i>0; i--) { frame = Cache->CachedFramesList[i-1]; // check if frame is empty if(!(Cache->FrameList[frame].BlockCount)) { WCacheRemoveFrame(Cache, Context, frame); } else { ASSERT(Cache->FrameList[frame].Frame); } } } if(!Cache->BlockCount) { return STATUS_SUCCESS; } // invoke media-specific limit-checker switch(Cache->Mode) { case WCACHE_MODE_RAM: return WCacheCheckLimitsRAM(Cache, Context, ReqLba, BCount); case WCACHE_MODE_ROM: case WCACHE_MODE_RW: return WCacheCheckLimitsRW(Cache, Context, ReqLba, BCount); case WCACHE_MODE_R: return WCacheCheckLimitsR(Cache, Context, ReqLba, BCount); } return STATUS_DRIVER_INTERNAL_ERROR; } // end WCacheCheckLimits() /* WCacheCheckLimitsRW() implements automatic flush and purge of unused blocks to keep enough free cache entries for newly read/written blocks for Random Access and ReWritable media using Read/Modify/Write technology. See also WCacheCheckLimits() Internal routine */ OSSTATUS __fastcall WCacheCheckLimitsRW( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN lba_t ReqLba, // first LBA to access/cache IN ULONG BCount // number of Blocks to access/cache ) { ULONG frame; lba_t firstLba; lba_t* List = Cache->CachedBlocksList; lba_t lastLba; lba_t Lba; // PCHAR tmp_buff = Cache->tmp_buff; ULONG firstPos; ULONG lastPos; ULONG BSh = Cache->BlockSizeSh; ULONG BS = Cache->BlockSize; ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes) ULONG PSs = Cache->PacketSize; ULONG try_count = 0; PW_CACHE_ENTRY block_array; OSSTATUS status; SIZE_T ReadBytes; ULONG FreeFrameCount = 0; // PVOID addr; PW_CACHE_ASYNC FirstWContext = NULL; PW_CACHE_ASYNC PrevWContext = NULL; ULONG chain_count = 0; if(Cache->FrameCount >= Cache->MaxFrames) { FreeFrameCount = Cache->FramesToKeepFree; } else if((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) + BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) { // we need free space to grow WCache without flushing data // for some period of time FreeFrameCount = Cache->FramesToKeepFree; goto Try_Another_Frame; } // remove(flush) some frames while((Cache->FrameCount >= Cache->MaxFrames) || FreeFrameCount) { Try_Another_Frame: if(!Cache->FrameCount || !Cache->BlockCount) { //ASSERT(!Cache->FrameCount); if(Cache->FrameCount) { UDFPrint(("ASSERT: Cache->FrameCount = %d, when 0 is expected\n", Cache->FrameCount)); } ASSERT(!Cache->BlockCount); if(!Cache->FrameCount) break; } frame = WCacheFindFrameToRelease(Cache); #if 0 if(Cache->FrameList[frame].WriteCount) { try_count++; if(try_count < MAX_TRIES_FOR_NA) goto Try_Another_Frame; } else { try_count = 0; } #else if(Cache->FrameList[frame].UpdateCount) { try_count = MAX_TRIES_FOR_NA; } else { try_count = 0; } #endif if(FreeFrameCount) FreeFrameCount--; firstLba = frame << Cache->BlocksPerFrameSh; lastLba = firstLba + Cache->BlocksPerFrame; firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba); lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba); block_array = Cache->FrameList[frame].Frame; if(!block_array) { UDFPrint(("Hmm...\n")); BrutePoint(); return STATUS_DRIVER_INTERNAL_ERROR; } while(firstPos < lastPos) { // flush packet Lba = List[firstPos] & ~(PSs-1); // write packet out or prepare and add to chain (if chained mode enabled) status = WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba, Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE); if(status != STATUS_PENDING) { // free memory WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs); } Lba += PSs; while((firstPos < lastPos) && (Lba > List[firstPos])) { firstPos++; } chain_count++; // write chained packets if(chain_count >= WCACHE_MAX_CHAIN) { WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE); chain_count = 0; } } // remove flushed blocks from all lists WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame); WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame); WCacheRemoveFrame(Cache, Context, frame); } // check if we try to read too much data if(BCount > Cache->MaxBlocks) { WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext); return STATUS_INVALID_PARAMETER; } // remove(flush) packet while((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) + BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) { try_count = 0; Try_Another_Block: Lba = WCacheFindLbaToRelease(Cache) & ~(PSs-1); if(Lba == WCACHE_INVALID_LBA) { ASSERT(!Cache->FrameCount); ASSERT(!Cache->BlockCount); break; } frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba); lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs); block_array = Cache->FrameList[frame].Frame; if(!block_array) { // write already prepared blocks to disk and return error WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext); ASSERT(FALSE); return STATUS_DRIVER_INTERNAL_ERROR; } // write packet out or prepare and add to chain (if chained mode enabled) status = WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba, Lba, BSh, BS, PS, PSs, &ReadBytes, (try_count >= MAX_TRIES_FOR_NA), ASYNC_STATE_NONE); if(status == STATUS_RETRY) { try_count++; goto Try_Another_Block; } // free memory WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs); WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs); WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs); // check if frame is empty if(!(Cache->FrameList[frame].BlockCount)) { WCacheRemoveFrame(Cache, Context, frame); } else { ASSERT(Cache->FrameList[frame].Frame); } chain_count++; if(chain_count >= WCACHE_MAX_CHAIN) { WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE); chain_count = 0; } } WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext); return STATUS_SUCCESS; } // end WCacheCheckLimitsRW() OSSTATUS __fastcall WCacheFlushBlocksRAM( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks PW_CACHE_ENTRY block_array, lba_t* List, ULONG firstPos, ULONG lastPos, BOOLEAN Purge ) { ULONG frame; lba_t Lba; lba_t PrevLba; lba_t firstLba; PCHAR tmp_buff = NULL; ULONG n; ULONG BSh = Cache->BlockSizeSh; ULONG BS = Cache->BlockSize; // ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes) ULONG PSs = Cache->PacketSize; SIZE_T _WrittenBytes; OSSTATUS status = STATUS_SUCCESS; frame = List[firstPos] >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; while(firstPos < lastPos) { // flush blocks ASSERT(Cache->FrameCount <= Cache->MaxFrames); Lba = List[firstPos]; if(!WCacheGetModFlag(block_array, Lba - firstLba)) { // free memory if(Purge) { WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, 1); } firstPos++; continue; } tmp_buff = Cache->tmp_buff; PrevLba = Lba; n=1; while((firstPos+n < lastPos) && (List[firstPos+n] == PrevLba+1)) { PrevLba++; if(!WCacheGetModFlag(block_array, PrevLba - firstLba)) break; DbgCopyMemory(tmp_buff + (n << BSh), (PVOID)WCacheSectorAddr(block_array, PrevLba - firstLba), BS); n++; if(n >= PSs) break; } if(n > 1) { DbgCopyMemory(tmp_buff, (PVOID)WCacheSectorAddr(block_array, Lba - firstLba), BS); } else { tmp_buff = (PCHAR)WCacheSectorAddr(block_array, Lba - firstLba); } // write sectors out status = Cache->WriteProc(Context, tmp_buff, n<CachedBlocksList; lba_t lastLba; lba_t Lba; // PCHAR tmp_buff = Cache->tmp_buff; ULONG firstPos; ULONG lastPos; // ULONG BSh = Cache->BlockSizeSh; // ULONG BS = Cache->BlockSize; // ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes) ULONG PSs = Cache->PacketSize; // ULONG try_count = 0; PW_CACHE_ENTRY block_array; // OSSTATUS status; ULONG FreeFrameCount = 0; // PVOID addr; if(Cache->FrameCount >= Cache->MaxFrames) { FreeFrameCount = Cache->FramesToKeepFree; } else if((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) + BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) { // we need free space to grow WCache without flushing data // for some period of time FreeFrameCount = Cache->FramesToKeepFree; goto Try_Another_Frame; } // remove(flush) some frames while((Cache->FrameCount >= Cache->MaxFrames) || FreeFrameCount) { ASSERT(Cache->FrameCount <= Cache->MaxFrames); Try_Another_Frame: if(!Cache->FrameCount || !Cache->BlockCount) { ASSERT(!Cache->FrameCount); ASSERT(!Cache->BlockCount); if(!Cache->FrameCount) break; } frame = WCacheFindFrameToRelease(Cache); #if 0 if(Cache->FrameList[frame].WriteCount) { try_count++; if(try_count < MAX_TRIES_FOR_NA) goto Try_Another_Frame; } else { try_count = 0; } #else /* if(Cache->FrameList[frame].UpdateCount) { try_count = MAX_TRIES_FOR_NA; } else { try_count = 0; } */ #endif if(FreeFrameCount) FreeFrameCount--; firstLba = frame << Cache->BlocksPerFrameSh; lastLba = firstLba + Cache->BlocksPerFrame; firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba); lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba); block_array = Cache->FrameList[frame].Frame; if(!block_array) { UDFPrint(("Hmm...\n")); BrutePoint(); return STATUS_DRIVER_INTERNAL_ERROR; } WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE); WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame); WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame); WCacheRemoveFrame(Cache, Context, frame); } // check if we try to read too much data if(BCount > Cache->MaxBlocks) { return STATUS_INVALID_PARAMETER; } // remove(flush) packet while((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) + BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) { // try_count = 0; //Try_Another_Block: ASSERT(Cache->FrameCount <= Cache->MaxFrames); Lba = WCacheFindLbaToRelease(Cache) & ~(PSs-1); if(Lba == WCACHE_INVALID_LBA) { ASSERT(!Cache->FrameCount); ASSERT(!Cache->BlockCount); break; } frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba); lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs); block_array = Cache->FrameList[frame].Frame; if(!block_array) { ASSERT(FALSE); return STATUS_DRIVER_INTERNAL_ERROR; } WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE); WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs); WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs); // check if frame is empty if(!(Cache->FrameList[frame].BlockCount)) { WCacheRemoveFrame(Cache, Context, frame); } else { ASSERT(Cache->FrameList[frame].Frame); } } return STATUS_SUCCESS; } // end WCacheCheckLimitsRAM() /* WCachePurgeAllRAM() Internal routine */ OSSTATUS __fastcall WCachePurgeAllRAM( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context // user-supplied context for IO callbacks ) { ULONG frame; lba_t firstLba; lba_t* List = Cache->CachedBlocksList; lba_t lastLba; ULONG firstPos; ULONG lastPos; PW_CACHE_ENTRY block_array; // OSSTATUS status; // remove(flush) some frames while(Cache->FrameCount) { frame = Cache->CachedFramesList[0]; firstLba = frame << Cache->BlocksPerFrameSh; lastLba = firstLba + Cache->BlocksPerFrame; firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba); lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba); block_array = Cache->FrameList[frame].Frame; if(!block_array) { UDFPrint(("Hmm...\n")); BrutePoint(); return STATUS_DRIVER_INTERNAL_ERROR; } WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, TRUE); WCacheRemoveRangeFromList(List, &(Cache->BlockCount), firstLba, Cache->BlocksPerFrame); WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame); WCacheRemoveFrame(Cache, Context, frame); } ASSERT(!Cache->FrameCount); ASSERT(!Cache->BlockCount); return STATUS_SUCCESS; } // end WCachePurgeAllRAM() /* WCacheFlushAllRAM() Internal routine */ OSSTATUS __fastcall WCacheFlushAllRAM( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context // user-supplied context for IO callbacks ) { ULONG frame; lba_t firstLba; lba_t* List = Cache->CachedBlocksList; lba_t lastLba; ULONG firstPos; ULONG lastPos; PW_CACHE_ENTRY block_array; // OSSTATUS status; // flush frames while(Cache->WriteCount) { frame = Cache->CachedModifiedBlocksList[0] >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; lastLba = firstLba + Cache->BlocksPerFrame; firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, firstLba); lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, lastLba); block_array = Cache->FrameList[frame].Frame; if(!block_array) { UDFPrint(("Hmm...\n")); BrutePoint(); return STATUS_DRIVER_INTERNAL_ERROR; } WCacheFlushBlocksRAM(Cache, Context, block_array, List, firstPos, lastPos, FALSE); WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), firstLba, Cache->BlocksPerFrame); } return STATUS_SUCCESS; } // end WCacheFlushAllRAM() /* WCachePreReadPacket__() reads & caches the whole packet containing requested LBA. This routine just caches data, it doesn't copy anything to user buffer. In general we have no user buffer here... ;) Public routine */ OSSTATUS WCachePreReadPacket__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN lba_t Lba // LBA to cache together with whole packet ) { ULONG frame; OSSTATUS status = STATUS_SUCCESS; PW_CACHE_ENTRY block_array; ULONG BSh = Cache->BlockSizeSh; ULONG BS = Cache->BlockSize; PCHAR addr; SIZE_T _ReadBytes; ULONG PS = Cache->PacketSize; // (in blocks) ULONG BCount = PS; ULONG i, n, err_count; BOOLEAN sector_added = FALSE; ULONG block_type; BOOLEAN zero = FALSE;//TRUE; /* ULONG first_zero=0, last_zero=0; BOOLEAN count_first_zero = TRUE; */ Lba &= ~(PS-1); frame = Lba >> Cache->BlocksPerFrameSh; i = Lba - (frame << Cache->BlocksPerFrameSh); // assume successful operation block_array = Cache->FrameList[frame].Frame; if(!block_array) { ASSERT(Cache->FrameCount < Cache->MaxFrames); block_array = WCacheInitFrame(Cache, Context, frame); if(!block_array) return STATUS_INSUFFICIENT_RESOURCES; } // skip cached extent (if any) n=0; while((n < BCount) && (n < Cache->BlocksPerFrame)) { addr = (PCHAR)WCacheSectorAddr(block_array, i+n); block_type = Cache->CheckUsedProc(Context, Lba+n); if(/*WCacheGetBadFlag(block_array,i+n)*/ block_type & WCACHE_BLOCK_BAD) { // bad packet. no pre-read return STATUS_DEVICE_DATA_ERROR; } if(!(block_type & WCACHE_BLOCK_ZERO)) { zero = FALSE; //count_first_zero = FALSE; //last_zero = 0; if(!addr) { // sector is not cached, stop search break; } } else { /* if(count_first_zero) { first_zero++; } last_zero++; */ } n++; } // do nothing if all sectors are already cached if(n < BCount) { // read whole packet if(!zero) { status = Cache->ReadProc(Context, Cache->tmp_buff_r, PS<tmp_buff_r, WCACHE_R_OP, NULL); } } else { status = STATUS_SUCCESS; //RtlZeroMemory(Cache->tmp_buff_r, PS<tmp_buff_r+(n<FrameList[frame].BlockCount++; } } else { // read sectors one by one and copy them to cache // unreadable sectors will be treated as zero-filled err_count = 0; for(n=0; nReadProc(Context, Cache->tmp_buff_r, BS, Lba+n, &_ReadBytes, PH_TMP_BUFFER); if(!OS_SUCCESS(status)) { status = WCacheRaiseIoError(Cache, Context, status, Lba+n, 1, Cache->tmp_buff_r, WCACHE_R_OP, NULL); if(!OS_SUCCESS(status)) { err_count++; } } if(!zero && OS_SUCCESS(status)) { DbgCopyMemory(addr, Cache->tmp_buff_r, BS); } else if(Cache->RememberBB) { RtlZeroMemory(addr, BS); /* if(!OS_SUCCESS(status)) { WCacheSetBadFlag(block_array,i); } */ } Cache->FrameList[frame].BlockCount++; if(err_count >= 2) { break; } } // _ReadBytes = n<CachedBlocksList, &(Cache->BlockCount), Lba, n); return status; } // end WCachePreReadPacket__() /* WCacheReadBlocks__() reads data from cache or read it form media and store in cache. Public routine */ OSSTATUS WCacheReadBlocks__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN PCHAR Buffer, // user-supplied buffer for read blocks IN lba_t Lba, // LBA to start read from IN ULONG BCount, // number of blocks to be read OUT PSIZE_T ReadBytes, // user-supplied pointer to ULONG that will // recieve number of actually read bytes IN BOOLEAN CachedOnly // specifies that cache is already locked ) { ULONG frame; ULONG i, saved_i, saved_BC = BCount, n; OSSTATUS status = STATUS_SUCCESS; PW_CACHE_ENTRY block_array; ULONG BSh = Cache->BlockSizeSh; SIZE_T BS = Cache->BlockSize; PCHAR addr; ULONG to_read, saved_to_read; // PCHAR saved_buff = Buffer; SIZE_T _ReadBytes; ULONG PS = Cache->PacketSize; ULONG MaxR = Cache->MaxBytesToRead; ULONG PacketMask = PS-1; // here we assume that Packet Size value is 2^n ULONG d; ULONG block_type; WcPrint(("WC:R %x (%x)\n", Lba, BCount)); (*ReadBytes) = 0; // check if we try to read too much data if(BCount >= Cache->MaxBlocks) { i = 0; if(CachedOnly) { status = STATUS_INVALID_PARAMETER; goto EO_WCache_R2; } while(TRUE) { status = WCacheReadBlocks__(Cache, Context, Buffer + (i<FirstLba) || (Lba + BCount - 1 > Cache->LastLba)) { status = Cache->ReadProc(Context, Buffer, BCount, Lba, ReadBytes, 0); if(!OS_SUCCESS(status)) { status = WCacheRaiseIoError(Cache, Context, status, Lba, BCount, Buffer, WCACHE_R_OP, NULL); } return status; } if(!CachedOnly) { ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE); } frame = Lba >> Cache->BlocksPerFrameSh; i = Lba - (frame << Cache->BlocksPerFrameSh); if(Cache->CacheWholePacket && (BCount < PS)) { if(!CachedOnly && !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba & ~(PS-1), PS*2)) ) { ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); return status; } } else { if(!CachedOnly && !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, BCount))) { ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); return status; } } if(!CachedOnly) { // convert to shared // ExConvertExclusiveToSharedLite(&(Cache->WCacheLock)); } // pre-read packet. It is very useful for // highly fragmented files if(Cache->CacheWholePacket && (BCount < PS)) { // status = WCacheReadBlocks__(Cache, Context, Cache->tmp_buff_r, Lba & (~PacketMask), PS, &_ReadBytes, TRUE); // we should not perform IO if user requested CachedOnly data if(!CachedOnly) { status = WCachePreReadPacket__(Cache, Context, Lba); } status = STATUS_SUCCESS; } // assume successful operation block_array = Cache->FrameList[frame].Frame; if(!block_array) { ASSERT(!CachedOnly); ASSERT(Cache->FrameCount < Cache->MaxFrames); block_array = WCacheInitFrame(Cache, Context, frame); if(!block_array) { status = STATUS_INSUFFICIENT_RESOURCES; goto EO_WCache_R; } } Cache->FrameList[frame].AccessCount++; while(BCount) { if(i >= Cache->BlocksPerFrame) { frame++; block_array = Cache->FrameList[frame].Frame; i -= Cache->BlocksPerFrame; } if(!block_array) { ASSERT(Cache->FrameCount < Cache->MaxFrames); block_array = WCacheInitFrame(Cache, Context, frame); if(!block_array) { status = STATUS_INSUFFICIENT_RESOURCES; goto EO_WCache_R; } } // 'read' cached extent (if any) // it is just copying while(BCount && (i < Cache->BlocksPerFrame) && (addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) { block_type = Cache->CheckUsedProc(Context, Lba+saved_BC-BCount); if(block_type & WCACHE_BLOCK_BAD) { //if(WCacheGetBadFlag(block_array,i)) { status = STATUS_DEVICE_DATA_ERROR; goto EO_WCache_R; } DbgCopyMemory(Buffer, addr, BS); Buffer += BS; *ReadBytes += BS; i++; BCount--; } // read non-cached packet-size-aligned extent (if any) // now we'll calculate total length & decide if it has enough size if(!((d = Lba+saved_BC-BCount) & PacketMask) && d ) { n = 0; while(BCount && (i < Cache->BlocksPerFrame) && (!WCacheSectorAddr(block_array, i)) ) { n++; BCount--; } BCount += n; n &= ~PacketMask; if(n>PS) { if(!OS_SUCCESS(status = Cache->ReadProc(Context, Buffer, BS*n, Lba+saved_BC-BCount, &_ReadBytes, 0))) { status = WCacheRaiseIoError(Cache, Context, status, Lba+saved_BC-BCount, n, Buffer, WCACHE_R_OP, NULL); if(!OS_SUCCESS(status)) { goto EO_WCache_R; } } // WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount); BCount -= n; Lba += saved_BC - BCount; saved_BC = BCount; i += n; Buffer += BS*n; *ReadBytes += BS*n; } // } else { // UDFPrint(("Unaligned\n")); } // read non-cached extent (if any) // firstable, we'll get total number of sectors to read to_read = 0; saved_i = i; d = BCount; while(d && (i < Cache->BlocksPerFrame) && (!WCacheSectorAddr(block_array, i)) ) { i++; to_read += BS; d--; } // read some not cached sectors if(to_read) { i = saved_i; saved_to_read = to_read; d = BCount - d; // split request if necessary if(saved_to_read > MaxR) { WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount); n = MaxR >> BSh; do { status = Cache->ReadProc(Context, Buffer, MaxR, i + (frame << Cache->BlocksPerFrameSh), &_ReadBytes, 0); *ReadBytes += _ReadBytes; if(!OS_SUCCESS(status)) { _ReadBytes &= ~(BS-1); BCount -= _ReadBytes >> BSh; saved_to_read -= _ReadBytes; Buffer += _ReadBytes; saved_BC = BCount; goto store_read_data_1; } Buffer += MaxR; saved_to_read -= MaxR; i += n; BCount -= n; d -= n; } while(saved_to_read > MaxR); saved_BC = BCount; } if(saved_to_read) { status = Cache->ReadProc(Context, Buffer, saved_to_read, i + (frame << Cache->BlocksPerFrameSh), &_ReadBytes, 0); *ReadBytes += _ReadBytes; if(!OS_SUCCESS(status)) { _ReadBytes &= ~(BS-1); BCount -= _ReadBytes >> BSh; saved_to_read -= _ReadBytes; Buffer += _ReadBytes; goto store_read_data_1; } Buffer += saved_to_read; saved_to_read = 0; BCount -= d; } store_read_data_1: // and now we'll copy them to cache // Buffer -= (to_read - saved_to_read); i = saved_i; while(to_read - saved_to_read) { block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG); if(!block_array[i].Sector) { BCount += to_read >> BSh; status = STATUS_INSUFFICIENT_RESOURCES; goto EO_WCache_R; } DbgCopyMemory(block_array[i].Sector, Buffer, BS); Cache->FrameList[frame].BlockCount++; i++; Buffer += BS; to_read -= BS; } if(!OS_SUCCESS(status)) goto EO_WCache_R; to_read = 0; } } EO_WCache_R: // we know the number of unread sectors if an error occured // so we can need to update BlockCount // return number of read bytes WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount); // Cache->FrameList[frame].BlockCount -= BCount; EO_WCache_R2: if(!CachedOnly) { ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); } return status; } // end WCacheReadBlocks__() /* WCacheWriteBlocks__() writes data to cache. Data is written directly to media if: 1) requested block is Packet-aligned 2) requested Lba(s) lays beyond cached area Public routine */ OSSTATUS WCacheWriteBlocks__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN PCHAR Buffer, // user-supplied buffer containing data to be written IN lba_t Lba, // LBA to start write from IN ULONG BCount, // number of blocks to be written OUT PSIZE_T WrittenBytes, // user-supplied pointer to ULONG that will // recieve number of actually written bytes IN BOOLEAN CachedOnly // specifies that cache is already locked ) { ULONG frame; ULONG i, saved_BC = BCount, n, d; OSSTATUS status = STATUS_SUCCESS; PW_CACHE_ENTRY block_array; ULONG BSh = Cache->BlockSizeSh; ULONG BS = Cache->BlockSize; PCHAR addr; // PCHAR saved_buff = Buffer; SIZE_T _WrittenBytes; ULONG PS = Cache->PacketSize; ULONG PacketMask = PS-1; // here we assume that Packet Size value is 2^n ULONG block_type; // BOOLEAN Aligned = FALSE; BOOLEAN WriteThrough = FALSE; lba_t WTh_Lba; ULONG WTh_BCount; WcPrint(("WC:W %x (%x)\n", Lba, BCount)); *WrittenBytes = 0; // UDFPrint(("BCount:%x\n",BCount)); // check if we try to read too much data if(BCount >= Cache->MaxBlocks) { i = 0; if(CachedOnly) { status = STATUS_INVALID_PARAMETER; goto EO_WCache_W2; } while(TRUE) { // UDFPrint((" BCount:%x\n",BCount)); status = WCacheWriteBlocks__(Cache, Context, Buffer + (i<FirstLba) || (Lba + BCount - 1 > Cache->LastLba)) { return STATUS_INVALID_PARAMETER; } if(!CachedOnly) { ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE); } frame = Lba >> Cache->BlocksPerFrameSh; i = Lba - (frame << Cache->BlocksPerFrameSh); if(!CachedOnly && !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, BCount))) { ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); return status; } // assume successful operation block_array = Cache->FrameList[frame].Frame; if(!block_array) { if(BCount && !(BCount & (PS-1)) && !(Lba & (PS-1)) && (Cache->Mode != WCACHE_MODE_R) && (i+BCount <= Cache->BlocksPerFrame) && !Cache->NoWriteThrough) { status = Cache->WriteProc(Context, Buffer, BCount<FrameCount < Cache->MaxFrames); block_array = WCacheInitFrame(Cache, Context, frame); if(!block_array) { status = STATUS_INSUFFICIENT_RESOURCES; goto EO_WCache_W; } } if(Cache->Mode == WCACHE_MODE_RAM && BCount && // !(Lba & (PS-1)) && (!(BCount & (PS-1)) || (BCount > PS)) ) { WriteThrough = TRUE; WTh_Lba = Lba; WTh_BCount = BCount; } else if(Cache->Mode == WCACHE_MODE_RAM && ((Lba & ~PacketMask) != ((Lba+BCount-1) & ~PacketMask)) ) { WriteThrough = TRUE; WTh_Lba = Lba & ~PacketMask; WTh_BCount = PS; } Cache->FrameList[frame].UpdateCount++; // UDFPrint((" BCount:%x\n",BCount)); while(BCount) { if(i >= Cache->BlocksPerFrame) { frame++; block_array = Cache->FrameList[frame].Frame; i -= Cache->BlocksPerFrame; } if(!block_array) { ASSERT(Cache->FrameCount < Cache->MaxFrames); block_array = WCacheInitFrame(Cache, Context, frame); if(!block_array) { status = STATUS_INSUFFICIENT_RESOURCES; goto EO_WCache_W; } } // 'write' cached extent (if any) // it is just copying while(BCount && (i < Cache->BlocksPerFrame) && (addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) { // UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",addr, Buffer, BS, BCount)); block_type = Cache->CheckUsedProc(Context, Lba+saved_BC-BCount); if(Cache->NoWriteBB && /*WCacheGetBadFlag(block_array,i)*/ (block_type & WCACHE_BLOCK_BAD)) { // bad packet. no cached write status = STATUS_DEVICE_DATA_ERROR; goto EO_WCache_W; } DbgCopyMemory(addr, Buffer, BS); WCacheSetModFlag(block_array, i); Buffer += BS; *WrittenBytes += BS; i++; BCount--; } // write non-cached not-aligned extent (if any) till aligned one while(BCount && (i & PacketMask) && (Cache->Mode != WCACHE_MODE_R) && (i < Cache->BlocksPerFrame) && (!WCacheSectorAddr(block_array, i)) ) { block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG); if(!block_array[i].Sector) { status = STATUS_INSUFFICIENT_RESOURCES; goto EO_WCache_W; } // UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",block_array[i].Sector, Buffer, BS, BCount)); DbgCopyMemory(block_array[i].Sector, Buffer, BS); WCacheSetModFlag(block_array, i); i++; Buffer += BS; *WrittenBytes += BS; BCount--; Cache->FrameList[frame].BlockCount ++; } // write non-cached packet-size-aligned extent (if any) // now we'll calculate total length & decide if has enough size if(!Cache->NoWriteThrough && ( !(i & PacketMask) || ((Cache->Mode == WCACHE_MODE_R) && (BCount >= PS)) )) { n = 0; while(BCount && (i < Cache->BlocksPerFrame) && (!WCacheSectorAddr(block_array, i)) ) { n++; BCount--; } BCount += n; n &= ~PacketMask; // if(!OS_SUCCESS(status = Cache->WriteProcAsync(Context, Buffer, BS*n, Lba+saved_BC-BCount, &_WrittenBytes, FALSE))) if(n) { // add previously written data to list d = saved_BC - BCount; WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, d); WCacheInsertRangeToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, d); Lba += d; saved_BC = BCount; while(n) { if(Cache->Mode == WCACHE_MODE_R) Cache->UpdateRelocProc(Context, Lba, NULL, PS); if(!OS_SUCCESS(status = Cache->WriteProc(Context, Buffer, PS<BlocksPerFrame) && (!WCacheSectorAddr(block_array, i)) ) { block_array[i].Sector = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG); if(!block_array[i].Sector) { status = STATUS_INSUFFICIENT_RESOURCES; goto EO_WCache_W; } // UDFPrint(("addr:%x:Buffer:%x:BS:%x:BCount:%x\n",block_array[i].Sector, Buffer, BS, BCount)); DbgCopyMemory(block_array[i].Sector, Buffer, BS); WCacheSetModFlag(block_array, i); i++; Buffer += BS; *WrittenBytes += BS; BCount--; Cache->FrameList[frame].BlockCount ++; } } EO_WCache_W: // we know the number of unread sectors if an error occured // so we can need to update BlockCount // return number of read bytes WCacheInsertRangeToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba, saved_BC - BCount); WCacheInsertRangeToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, saved_BC - BCount); if(WriteThrough && !BCount) { ULONG d; // lba_t lastLba; ULONG firstPos; ULONG lastPos; BCount = WTh_BCount; Lba = WTh_Lba; while(BCount) { frame = Lba >> Cache->BlocksPerFrameSh; // firstLba = frame << Cache->BlocksPerFrameSh; firstPos = WCacheGetSortedListIndex(Cache->BlockCount, Cache->CachedBlocksList, Lba); d = min(Lba+BCount, (frame+1) << Cache->BlocksPerFrameSh) - Lba; lastPos = WCacheGetSortedListIndex(Cache->BlockCount, Cache->CachedBlocksList, Lba+d); block_array = Cache->FrameList[frame].Frame; if(!block_array) { ASSERT(FALSE); BCount -= d; Lba += d; continue; } status = WCacheFlushBlocksRAM(Cache, Context, block_array, Cache->CachedBlocksList, firstPos, lastPos, FALSE); WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, d); BCount -= d; Lba += d; } } EO_WCache_W2: if(!CachedOnly) { ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); } return status; } // end WCacheWriteBlocks__() /* WCacheFlushAll__() copies all data stored in cache to media. Flushed blocks are kept in cache. Public routine */ VOID WCacheFlushAll__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context) // user-supplied context for IO callbacks { if(!(Cache->ReadProc)) return; ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE); switch(Cache->Mode) { case WCACHE_MODE_RAM: WCacheFlushAllRAM(Cache, Context); break; case WCACHE_MODE_ROM: case WCACHE_MODE_RW: WCacheFlushAllRW(Cache, Context); break; case WCACHE_MODE_R: WCachePurgeAllR(Cache, Context); break; } ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); return; } // end WCacheFlushAll__() /* WCachePurgeAll__() copies all data stored in cache to media. Flushed blocks are removed cache. Public routine */ VOID WCachePurgeAll__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context) // user-supplied context for IO callbacks { if(!(Cache->ReadProc)) return; ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE); switch(Cache->Mode) { case WCACHE_MODE_RAM: WCachePurgeAllRAM(Cache, Context); break; case WCACHE_MODE_ROM: case WCACHE_MODE_RW: WCachePurgeAllRW(Cache, Context); break; case WCACHE_MODE_R: WCachePurgeAllR(Cache, Context); break; } ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); return; } // end WCachePurgeAll__() /* WCachePurgeAllRW() copies modified blocks from cache to media and removes them from cache This routine can be used for RAM, RW and ROM media. For ROM media blocks are just removed. Internal routine */ VOID __fastcall WCachePurgeAllRW( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context) // user-supplied context for IO callbacks { ULONG frame; lba_t firstLba; lba_t* List = Cache->CachedBlocksList; lba_t Lba; // ULONG firstPos; // ULONG lastPos; ULONG BSh = Cache->BlockSizeSh; ULONG BS = Cache->BlockSize; ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes) ULONG PSs = Cache->PacketSize; PW_CACHE_ENTRY block_array; // OSSTATUS status; SIZE_T ReadBytes; PW_CACHE_ASYNC FirstWContext = NULL; PW_CACHE_ASYNC PrevWContext = NULL; ULONG chain_count = 0; if(!(Cache->ReadProc)) return; while(Cache->BlockCount) { Lba = List[0] & ~(PSs-1); frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; // firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba); // lastPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba+PSs); block_array = Cache->FrameList[frame].Frame; if(!block_array) { BrutePoint(); return; } WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba, Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE); // free memory WCacheFreePacket(Cache, frame, block_array, Lba-firstLba, PSs); WCacheRemoveRangeFromList(List, &(Cache->BlockCount), Lba, PSs); WCacheRemoveRangeFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba, PSs); // check if frame is empty if(!(Cache->FrameList[frame].BlockCount)) { WCacheRemoveFrame(Cache, Context, frame); } else { ASSERT(Cache->FrameList[frame].Frame); } chain_count++; if(chain_count >= WCACHE_MAX_CHAIN) { WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE); chain_count = 0; } } WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext); return; } // end WCachePurgeAllRW() /* WCacheFlushAllRW() copies modified blocks from cache to media. All blocks are not removed from cache. This routine can be used for RAM, RW and ROM media. Internal routine */ VOID __fastcall WCacheFlushAllRW( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context) // user-supplied context for IO callbacks { ULONG frame; lba_t firstLba; lba_t* List = Cache->CachedModifiedBlocksList; lba_t Lba; // ULONG firstPos; // ULONG lastPos; ULONG BSh = Cache->BlockSizeSh; ULONG BS = Cache->BlockSize; ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes) ULONG PSs = Cache->PacketSize; ULONG BFs = Cache->BlocksPerFrameSh; PW_CACHE_ENTRY block_array; // OSSTATUS status; SIZE_T ReadBytes; PW_CACHE_ASYNC FirstWContext = NULL; PW_CACHE_ASYNC PrevWContext = NULL; ULONG i; ULONG chain_count = 0; if(!(Cache->ReadProc)) return; // walk through modified blocks while(Cache->WriteCount) { Lba = List[0] & ~(PSs-1); frame = Lba >> BFs; firstLba = frame << BFs; // firstPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba); // lastPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba+PSs); block_array = Cache->FrameList[frame].Frame; if(!block_array) { BrutePoint(); continue;; } // queue modify request WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba, Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE); // clear MODIFIED flag for queued blocks WCacheRemoveRangeFromList(List, &(Cache->WriteCount), Lba, PSs); Lba -= firstLba; for(i=0; i= WCACHE_MAX_CHAIN) { WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE); chain_count = 0; } } WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE); #ifdef DBG #if 1 // check consistency List = Cache->CachedBlocksList; for(i=0; iBlockCount; i++) { Lba = List[i] /*& ~(PSs-1)*/; frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; block_array = Cache->FrameList[frame].Frame; if(!block_array) { BrutePoint(); } ASSERT(!WCacheGetModFlag(block_array, Lba-firstLba)); } #endif // 1 #endif // DBG return; } // end WCacheFlushAllRW() /* WCacheRelease__() frees all allocated memory blocks and deletes synchronization resources Public routine */ VOID WCacheRelease__( IN PW_CACHE Cache // pointer to the Cache Control structure ) { ULONG i, j, k; PW_CACHE_ENTRY block_array; Cache->Tag = 0xDEADCACE; if(!(Cache->ReadProc)) return; // ASSERT(Cache->Tag == 0xCAC11E00); ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE); for(i=0; iFrameCount; i++) { j = Cache->CachedFramesList[i]; block_array = Cache->FrameList[j].Frame; if(block_array) { for(k=0; kBlocksPerFrame; k++) { if(WCacheSectorAddr(block_array, k)) { WCacheFreeSector(j, k); } } MyFreePool__(block_array); } } if(Cache->FrameList) MyFreePool__(Cache->FrameList); if(Cache->CachedBlocksList) MyFreePool__(Cache->CachedBlocksList); if(Cache->CachedModifiedBlocksList) MyFreePool__(Cache->CachedModifiedBlocksList); if(Cache->CachedFramesList) MyFreePool__(Cache->CachedFramesList); if(Cache->tmp_buff_r) MyFreePool__(Cache->tmp_buff_r); if(Cache->CachedFramesList) MyFreePool__(Cache->tmp_buff); if(Cache->CachedFramesList) MyFreePool__(Cache->reloc_tab); ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); ExDeleteResourceLite(&(Cache->WCacheLock)); RtlZeroMemory(Cache, sizeof(W_CACHE)); return; } // end WCacheRelease__() /* WCacheIsInitialized__() checks if the pointer supplied points to initialized cache structure. Public routine */ BOOLEAN WCacheIsInitialized__( IN PW_CACHE Cache ) { return (Cache->ReadProc != NULL); } // end WCacheIsInitialized__() OSSTATUS WCacheFlushBlocksRW( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN lba_t _Lba, // LBA to start flush from IN ULONG BCount // number of blocks to be flushed ) { ULONG frame; lba_t firstLba; lba_t* List = Cache->CachedModifiedBlocksList; lba_t Lba; // ULONG firstPos; // ULONG lastPos; ULONG BSh = Cache->BlockSizeSh; ULONG BS = Cache->BlockSize; ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes) ULONG PSs = Cache->PacketSize; ULONG BFs = Cache->BlocksPerFrameSh; PW_CACHE_ENTRY block_array; // OSSTATUS status; SIZE_T ReadBytes; PW_CACHE_ASYNC FirstWContext = NULL; PW_CACHE_ASYNC PrevWContext = NULL; ULONG i; ULONG chain_count = 0; lba_t lim; if(!(Cache->ReadProc)) return STATUS_INVALID_PARAMETER; // walk through modified blocks lim = (_Lba+BCount+PSs-1) & ~(PSs-1); for(Lba = _Lba & ~(PSs-1);Lba < lim ; Lba += PSs) { frame = Lba >> BFs; firstLba = frame << BFs; // firstPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba); // lastPos = WCacheGetSortedListIndex(Cache->WriteCount, List, Lba+PSs); block_array = Cache->FrameList[frame].Frame; if(!block_array) { // not cached block may be requested for flush Lba += (1 << BFs) - PSs; continue; } // queue modify request WCacheUpdatePacket(Cache, Context, &FirstWContext, &PrevWContext, block_array, firstLba, Lba, BSh, BS, PS, PSs, &ReadBytes, TRUE, ASYNC_STATE_NONE); // clear MODIFIED flag for queued blocks WCacheRemoveRangeFromList(List, &(Cache->WriteCount), Lba, PSs); Lba -= firstLba; for(i=0; i= WCACHE_MAX_CHAIN) { WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE); chain_count = 0; } } WCacheUpdatePacketComplete(Cache, Context, &FirstWContext, &PrevWContext, FALSE); /* if(Cache->Mode != WCACHE_MODE_RAM) return STATUS_SUCCESS; */ return STATUS_SUCCESS; } // end WCacheFlushBlocksRW() /* WCacheFlushBlocks__() copies specified blocks stored in cache to media. Flushed blocks are kept in cache. Public routine */ OSSTATUS WCacheFlushBlocks__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN lba_t Lba, // LBA to start flush from IN ULONG BCount // number of blocks to be flushed ) { OSSTATUS status; if(!(Cache->ReadProc)) return STATUS_INVALID_PARAMETER; ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE); // check if we try to access beyond cached area if((Lba < Cache->FirstLba) || (Lba+BCount-1 > Cache->LastLba)) { UDFPrint(("LBA %#x (%x) is beyond cacheable area\n", Lba, BCount)); BrutePoint(); status = STATUS_INVALID_PARAMETER; goto EO_WCache_F; } switch(Cache->Mode) { case WCACHE_MODE_RAM: // WCacheFlushBlocksRW(Cache, Context); // break; case WCACHE_MODE_ROM: case WCACHE_MODE_RW: status = WCacheFlushBlocksRW(Cache, Context, Lba, BCount); break; case WCACHE_MODE_R: status = STATUS_SUCCESS; break; } EO_WCache_F: ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); return status; } // end WCacheFlushBlocks__() /* WCacheDirect__() returns pointer to memory block where requested block is stored in. If no #CachedOnly flag specified this routine locks cache, otherwise it assumes that cache is already locked by previous call to WCacheStartDirect__(). Cache can be unlocked by WCacheEODirect__(). Using this routine caller can access cached block directly in memory without Read_to_Tmp and Modify/Write steps. Public routine */ OSSTATUS WCacheDirect__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN lba_t Lba, // LBA of block to get pointer to IN BOOLEAN Modified, // indicates that block will be modified OUT PCHAR* CachedBlock, // address for pointer to cached block to be stored in IN BOOLEAN CachedOnly // specifies that cache is already locked ) { ULONG frame; ULONG i; OSSTATUS status = STATUS_SUCCESS; PW_CACHE_ENTRY block_array; ULONG BS = Cache->BlockSize; PCHAR addr; SIZE_T _ReadBytes; ULONG block_type; WcPrint(("WC:%sD %x (1)\n", Modified ? "W" : "R", Lba)); // lock cache if nececcary if(!CachedOnly) { ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE); } // check if we try to access beyond cached area if((Lba < Cache->FirstLba) || (Lba > Cache->LastLba)) { UDFPrint(("LBA %#x is beyond cacheable area\n", Lba)); BrutePoint(); status = STATUS_INVALID_PARAMETER; goto EO_WCache_D; } frame = Lba >> Cache->BlocksPerFrameSh; i = Lba - (frame << Cache->BlocksPerFrameSh); // check if we have enough space to store requested block if(!CachedOnly && !OS_SUCCESS(status = WCacheCheckLimits(Cache, Context, Lba, 1))) { BrutePoint(); goto EO_WCache_D; } // small updates are more important block_array = Cache->FrameList[frame].Frame; if(Modified) { Cache->FrameList[frame].UpdateCount+=8; } else { Cache->FrameList[frame].AccessCount+=8; } if(!block_array) { ASSERT(Cache->FrameCount < Cache->MaxFrames); block_array = WCacheInitFrame(Cache, Context, frame); if(!block_array) { status = STATUS_INSUFFICIENT_RESOURCES; goto EO_WCache_D; } } // check if requested block is already cached if( !(addr = (PCHAR)WCacheSectorAddr(block_array, i)) ) { // block is not cached // allocate memory and read block from media // do not set block_array[i].Sector here, because if media access fails and recursive access to cache // comes, this block should not be marked as 'cached' addr = (PCHAR)DbgAllocatePoolWithTag(CACHED_BLOCK_MEMORY_TYPE, BS, MEM_WCBUF_TAG); if(!addr) { status = STATUS_INSUFFICIENT_RESOURCES; goto EO_WCache_D; } block_type = Cache->CheckUsedProc(Context, Lba); if(block_type == WCACHE_BLOCK_USED) { status = Cache->ReadProc(Context, addr, BS, Lba, &_ReadBytes, PH_TMP_BUFFER); if(Cache->RememberBB) { if(!OS_SUCCESS(status)) { RtlZeroMemory(addr, BS); //WCacheSetBadFlag(block_array,i); } } } else { if(block_type & WCACHE_BLOCK_BAD) { DbgFreePool(addr); addr = NULL; status = STATUS_DEVICE_DATA_ERROR; goto EO_WCache_D; } if(!(block_type & WCACHE_BLOCK_ZERO)) { BrutePoint(); } status = STATUS_SUCCESS; RtlZeroMemory(addr, BS); } // now add pointer to buffer to common storage block_array[i].Sector = addr; WCacheInsertItemToList(Cache->CachedBlocksList, &(Cache->BlockCount), Lba); if(Modified) { WCacheInsertItemToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba); WCacheSetModFlag(block_array, i); } Cache->FrameList[frame].BlockCount ++; } else { // block is not cached // just return pointer block_type = Cache->CheckUsedProc(Context, Lba); if(block_type & WCACHE_BLOCK_BAD) { //if(WCacheGetBadFlag(block_array,i)) { // bad packet. no pre-read status = STATUS_DEVICE_DATA_ERROR; goto EO_WCache_D; } #ifndef UDF_CHECK_UTIL ASSERT(block_type & WCACHE_BLOCK_USED); #else if(!(block_type & WCACHE_BLOCK_USED)) { UDFPrint(("LBA %#x is not marked as used\n", Lba)); } #endif if(Modified && !WCacheGetModFlag(block_array, i)) { WCacheInsertItemToList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba); WCacheSetModFlag(block_array, i); } } (*CachedBlock) = addr; EO_WCache_D: return status; } // end WCacheDirect__() /* WCacheEODirect__() must be used to unlock cache after calls to to WCacheStartDirect__(). Public routine */ OSSTATUS WCacheEODirect__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context // user-supplied context for IO callbacks ) { ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); return STATUS_SUCCESS; } // end WCacheEODirect__() /* WCacheStartDirect__() locks cache for exclusive use. Using this routine caller can access cached block directly in memory without Read_to_Tmp and Modify/Write steps. See also WCacheDirect__() Cache can be unlocked by WCacheEODirect__(). Public routine */ OSSTATUS WCacheStartDirect__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN BOOLEAN Exclusive // lock cache for exclusive use, // currently must be TRUE. ) { if(Exclusive) { ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE); } else { BrutePoint(); ExAcquireResourceSharedLite(&(Cache->WCacheLock), TRUE); } return STATUS_SUCCESS; } // end WCacheStartDirect__() /* WCacheIsCached__() checks if requested blocks are immediately available. Cache must be previously locked for exclusive use with WCacheStartDirect__(). Using this routine caller can access cached block directly in memory without Read_to_Tmp and Modify/Write steps. See also WCacheDirect__(). Cache can be unlocked by WCacheEODirect__(). Public routine */ BOOLEAN WCacheIsCached__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN lba_t Lba, // LBA to start check from IN ULONG BCount // number of blocks to be checked ) { ULONG frame; ULONG i; PW_CACHE_ENTRY block_array; // check if we try to access beyond cached area if((Lba < Cache->FirstLba) || (Lba + BCount - 1 > Cache->LastLba)) { return FALSE; } frame = Lba >> Cache->BlocksPerFrameSh; i = Lba - (frame << Cache->BlocksPerFrameSh); block_array = Cache->FrameList[frame].Frame; if(!block_array) { return FALSE; } while(BCount) { if(i >= Cache->BlocksPerFrame) { frame++; block_array = Cache->FrameList[frame].Frame; i -= Cache->BlocksPerFrame; } if(!block_array) { return FALSE; } // 'read' cached extent (if any) while(BCount && (i < Cache->BlocksPerFrame) && WCacheSectorAddr(block_array, i) && /*!WCacheGetBadFlag(block_array, i)*/ /*!(Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_BAD)*/ TRUE ) { i++; BCount--; Lba++; } if(BCount && (i < Cache->BlocksPerFrame) /*&& (!WCacheSectorAddr(block_array, i))*/ ) { return FALSE; } } return TRUE; } // end WCacheIsCached__() /* WCacheCheckLimitsR() implements automatic flush and purge of unused blocks to keep enough free cache entries for newly read/written blocks for WORM media. See also WCacheCheckLimits() Internal routine */ OSSTATUS __fastcall WCacheCheckLimitsR( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context, // user-supplied context for IO callbacks IN lba_t ReqLba, // first LBA to access/cache IN ULONG BCount // number of Blocks to access/cache ) { ULONG frame; lba_t firstLba; lba_t* List = Cache->CachedBlocksList; lba_t Lba; PCHAR tmp_buff = Cache->tmp_buff; ULONG firstPos; ULONG BSh = Cache->BlockSizeSh; ULONG BS = Cache->BlockSize; ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes) ULONG PSs = Cache->PacketSize; ULONG i; PW_CACHE_ENTRY block_array; BOOLEAN mod; OSSTATUS status; SIZE_T ReadBytes; ULONG MaxReloc = Cache->PacketSize; PULONG reloc_tab = Cache->reloc_tab; // check if we try to read too much data if(BCount > Cache->MaxBlocks) { return STATUS_INVALID_PARAMETER; } // remove(flush) packets from entire frame(s) while( ((Cache->BlockCount + WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba) + BCount - WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba+BCount)) > Cache->MaxBlocks) || (Cache->FrameCount >= Cache->MaxFrames) ) { WCCL_retry_1: Lba = WCacheFindLbaToRelease(Cache); if(Lba == WCACHE_INVALID_LBA) { ASSERT(!Cache->FrameCount); ASSERT(!Cache->BlockCount); break; } frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; firstPos = WCacheGetSortedListIndex(Cache->BlockCount, List, Lba); block_array = Cache->FrameList[frame].Frame; if(!block_array) { return STATUS_DRIVER_INTERNAL_ERROR; } // check if modified mod = WCacheGetModFlag(block_array, Lba - firstLba); // read/modify/write if(mod && (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) { if(Cache->WriteCount < MaxReloc) goto WCCL_retry_1; firstPos = WCacheGetSortedListIndex(Cache->WriteCount, Cache->CachedModifiedBlocksList, Lba); if(!block_array) { return STATUS_DRIVER_INTERNAL_ERROR; } // prepare packet & reloc table for(i=0; iCachedModifiedBlocksList[firstPos]; frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; block_array = Cache->FrameList[frame].Frame; DbgCopyMemory(tmp_buff + (i << BSh), (PVOID)WCacheSectorAddr(block_array, Lba-firstLba), BS); reloc_tab[i] = Lba; WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba); WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba); // mark as non-cached & free pool WCacheFreeSector(frame, Lba-firstLba); // check if frame is empty if(!Cache->FrameList[frame].BlockCount) { WCacheRemoveFrame(Cache, Context, frame); } if(firstPos >= Cache->WriteCount) firstPos=0; } // write packet // status = Cache->WriteProcAsync(Context, tmp_buff, PS, Lba, &ReadBytes, FALSE); Cache->UpdateRelocProc(Context, NULL, reloc_tab, MaxReloc); status = Cache->WriteProc(Context, tmp_buff, PS, NULL, &ReadBytes, 0); if(!OS_SUCCESS(status)) { status = WCacheRaiseIoError(Cache, Context, status, NULL, PSs, tmp_buff, WCACHE_W_OP, NULL); } } else { if((i = Cache->BlockCount - Cache->WriteCount) > MaxReloc) i = MaxReloc; // discard blocks for(; i; i--) { Lba = List[firstPos]; frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; block_array = Cache->FrameList[frame].Frame; if( (mod = WCacheGetModFlag(block_array, Lba - firstLba)) && (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED) ) continue; WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba); if(mod) WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba); // mark as non-cached & free pool WCacheFreeSector(frame, Lba-firstLba); // check if frame is empty if(!Cache->FrameList[frame].BlockCount) { WCacheRemoveFrame(Cache, Context, frame); } if(firstPos >= Cache->WriteCount) firstPos=0; } } } return STATUS_SUCCESS; } // end WCacheCheckLimitsR() /* WCachePurgeAllR() copies modified blocks from cache to media and removes them from cache This routine can be used for R media only. Internal routine */ VOID __fastcall WCachePurgeAllR( IN PW_CACHE Cache, // pointer to the Cache Control structure IN PVOID Context) // user-supplied context for IO callbacks { ULONG frame; lba_t firstLba; lba_t* List = Cache->CachedBlocksList; lba_t Lba; PCHAR tmp_buff = Cache->tmp_buff; ULONG BSh = Cache->BlockSizeSh; ULONG BS = Cache->BlockSize; // ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes) // ULONG PSs = Cache->PacketSize; PW_CACHE_ENTRY block_array; BOOLEAN mod; OSSTATUS status; SIZE_T ReadBytes; ULONG MaxReloc = Cache->PacketSize; PULONG reloc_tab = Cache->reloc_tab; ULONG RelocCount = 0; BOOLEAN IncompletePacket; ULONG i=0; ULONG PacketTail; while(Cache->WriteCount < Cache->BlockCount) { Lba = List[i]; frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; block_array = Cache->FrameList[frame].Frame; if(!block_array) { BrutePoint(); return; } // check if modified mod = WCacheGetModFlag(block_array, Lba - firstLba); // just discard if(!mod || !(Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) { // mark as non-cached & free pool if(WCacheSectorAddr(block_array,Lba-firstLba)) { WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba); if(mod) WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba); // mark as non-cached & free pool WCacheFreeSector(frame, Lba-firstLba); // check if frame is empty if(!Cache->FrameList[frame].BlockCount) { WCacheRemoveFrame(Cache, Context, frame); } } else { BrutePoint(); } } else { i++; } } PacketTail = Cache->WriteCount & (MaxReloc-1); IncompletePacket = (Cache->WriteCount >= MaxReloc) ? FALSE : TRUE; // remove(flush) packet while((Cache->WriteCount > PacketTail) || (Cache->WriteCount && IncompletePacket)) { Lba = List[0]; frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; block_array = Cache->FrameList[frame].Frame; if(!block_array) { BrutePoint(); return; } // check if modified mod = WCacheGetModFlag(block_array, Lba - firstLba); // pack/reloc/write if(mod) { DbgCopyMemory(tmp_buff + (RelocCount << BSh), (PVOID)WCacheSectorAddr(block_array, Lba-firstLba), BS); reloc_tab[RelocCount] = Lba; RelocCount++; // write packet if((RelocCount >= MaxReloc) || (Cache->BlockCount == 1)) { // status = Cache->WriteProcAsync(Context, tmp_buff, PS, Lba, &ReadBytes, FALSE); Cache->UpdateRelocProc(Context, NULL, reloc_tab, RelocCount); status = Cache->WriteProc(Context, tmp_buff, RelocCount<CachedModifiedBlocksList, &(Cache->WriteCount), Lba); } else { BrutePoint(); } // mark as non-cached & free pool if(WCacheSectorAddr(block_array,Lba-firstLba)) { WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba); // mark as non-cached & free pool WCacheFreeSector(frame, Lba-firstLba); // check if frame is empty if(!Cache->FrameList[frame].BlockCount) { WCacheRemoveFrame(Cache, Context, frame); } } else { BrutePoint(); } } } // end WCachePurgeAllR() /* WCacheSetMode__() changes cache operating mode (ROM/R/RW/RAM). Public routine */ OSSTATUS WCacheSetMode__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN ULONG Mode // cache mode/media type to be used ) { if(Mode > WCACHE_MODE_MAX) return STATUS_INVALID_PARAMETER; Cache->Mode = Mode; return STATUS_SUCCESS; } // end WCacheSetMode__() /* WCacheGetMode__() returns cache operating mode (ROM/R/RW/RAM). Public routine */ ULONG WCacheGetMode__( IN PW_CACHE Cache ) { return Cache->Mode; } // end WCacheGetMode__() /* WCacheGetWriteBlockCount__() returns number of modified blocks, those are not flushed to media. Is usually used to preallocate blocks for relocation table on WORM (R) media. Public routine */ ULONG WCacheGetWriteBlockCount__( IN PW_CACHE Cache ) { return Cache->WriteCount; } // end WCacheGetWriteBlockCount__() /* WCacheSyncReloc__() builds list of all modified blocks, currently stored in cache. For each modified block WCacheSyncReloc__() calls user-supplied callback routine in order to update relocation table on WORM (R) media. Public routine */ VOID WCacheSyncReloc__( IN PW_CACHE Cache, IN PVOID Context) { ULONG frame; lba_t firstLba; lba_t* List = Cache->CachedBlocksList; lba_t Lba; // ULONG BSh = Cache->BlockSizeSh; // ULONG BS = Cache->BlockSize; // ULONG PS = BS << Cache->PacketSizeSh; // packet size (bytes) // ULONG PSs = Cache->PacketSize; PW_CACHE_ENTRY block_array; BOOLEAN mod; ULONG MaxReloc = Cache->PacketSize; PULONG reloc_tab = Cache->reloc_tab; ULONG RelocCount = 0; BOOLEAN IncompletePacket; IncompletePacket = (Cache->WriteCount >= MaxReloc) ? FALSE : TRUE; // enumerate modified blocks for(ULONG i=0; IncompletePacket && (iBlockCount); i++) { Lba = List[i]; frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; block_array = Cache->FrameList[frame].Frame; if(!block_array) { return; } // check if modified mod = WCacheGetModFlag(block_array, Lba - firstLba); // update relocation table for modified sectors if(mod && (Cache->CheckUsedProc(Context, Lba) & WCACHE_BLOCK_USED)) { reloc_tab[RelocCount] = Lba; RelocCount++; if(RelocCount >= Cache->WriteCount) { Cache->UpdateRelocProc(Context, NULL, reloc_tab, RelocCount); break; } } } } // end WCacheSyncReloc__() /* WCacheDiscardBlocks__() removes specified blocks from cache. Blocks are not flushed to media. Public routine */ VOID WCacheDiscardBlocks__( IN PW_CACHE Cache, IN PVOID Context, IN lba_t ReqLba, IN ULONG BCount ) { ULONG frame; lba_t firstLba; lba_t* List; lba_t Lba; PW_CACHE_ENTRY block_array; BOOLEAN mod; ULONG i; ExAcquireResourceExclusiveLite(&(Cache->WCacheLock), TRUE); UDFPrint((" Discard req: %x@%x\n",BCount, ReqLba)); List = Cache->CachedBlocksList; if(!List) { ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); return; } i = WCacheGetSortedListIndex(Cache->BlockCount, List, ReqLba); // enumerate requested blocks while((List[i] < (ReqLba+BCount)) && (i < Cache->BlockCount)) { Lba = List[i]; frame = Lba >> Cache->BlocksPerFrameSh; firstLba = frame << Cache->BlocksPerFrameSh; block_array = Cache->FrameList[frame].Frame; if(!block_array) { ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); BrutePoint(); return; } // check if modified mod = WCacheGetModFlag(block_array, Lba - firstLba); // just discard // mark as non-cached & free pool if(WCacheSectorAddr(block_array,Lba-firstLba)) { WCacheRemoveItemFromList(List, &(Cache->BlockCount), Lba); if(mod) WCacheRemoveItemFromList(Cache->CachedModifiedBlocksList, &(Cache->WriteCount), Lba); // mark as non-cached & free pool WCacheFreeSector(frame, Lba-firstLba); // check if frame is empty if(!Cache->FrameList[frame].BlockCount) { WCacheRemoveFrame(Cache, Context, frame); } else { ASSERT(Cache->FrameList[frame].Frame); } } else { // we should never get here !!! // getting this part of code means that we have // placed non-cached block in CachedBlocksList BrutePoint(); } } ExReleaseResourceForThreadLite(&(Cache->WCacheLock), ExGetCurrentResourceThread()); } // end WCacheDiscardBlocks__() OSSTATUS WCacheCompleteAsync__( IN PVOID WContext, IN OSSTATUS Status ) { PW_CACHE_ASYNC AsyncCtx = (PW_CACHE_ASYNC)WContext; // PW_CACHE Cache = AsyncCtx->Cache; AsyncCtx->PhContext.IosbToUse.Status = Status; KeSetEvent(&(AsyncCtx->PhContext.event), 0, FALSE); return STATUS_SUCCESS; } // end WCacheSetMode__() /* WCacheDecodeFlags() updates internal BOOLEANs according to Flags Internal routine */ OSSTATUS __fastcall WCacheDecodeFlags( IN PW_CACHE Cache, // pointer to the Cache Control structure IN ULONG Flags // cache mode flags ) { //ULONG OldFlags; if(Flags & ~WCACHE_VALID_FLAGS) { UDFPrint(("Invalid flags: %x\n", Flags & ~WCACHE_VALID_FLAGS)); return STATUS_INVALID_PARAMETER; } Cache->CacheWholePacket = (Flags & WCACHE_CACHE_WHOLE_PACKET) ? TRUE : FALSE; Cache->DoNotCompare = (Flags & WCACHE_DO_NOT_COMPARE) ? TRUE : FALSE; Cache->Chained = (Flags & WCACHE_CHAINED_IO) ? TRUE : FALSE; Cache->RememberBB = (Flags & WCACHE_MARK_BAD_BLOCKS) ? TRUE : FALSE; if(Cache->RememberBB) { Cache->NoWriteBB = (Flags & WCACHE_RO_BAD_BLOCKS) ? TRUE : FALSE; } Cache->NoWriteThrough = (Flags & WCACHE_NO_WRITE_THROUGH) ? TRUE : FALSE; Cache->Flags = Flags; return STATUS_SUCCESS; } /* WCacheChFlags__() changes cache flags. Public routine */ ULONG WCacheChFlags__( IN PW_CACHE Cache, // pointer to the Cache Control structure IN ULONG SetFlags, // cache mode/media type to be set IN ULONG ClrFlags // cache mode/media type to be cleared ) { ULONG Flags; if(SetFlags || ClrFlags) { Flags = (Cache->Flags & ~ClrFlags) | SetFlags; if(!OS_SUCCESS(WCacheDecodeFlags(Cache, Flags))) { return -1; } } else { return Cache->Flags; } return Flags; } // end WCacheSetMode__()