mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +00:00
1062 lines
30 KiB
C++
1062 lines
30 KiB
C++
////////////////////////////////////////////////////////////////////
|
|
// 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.
|
|
////////////////////////////////////////////////////////////////////
|
|
/*
|
|
Module name:
|
|
|
|
remap.cpp
|
|
|
|
Abstract:
|
|
|
|
This file contains filesystem-specific routines
|
|
responsible for disk space management
|
|
|
|
*/
|
|
|
|
#include "udf.h"
|
|
|
|
#define UDF_BUG_CHECK_ID UDF_FILE_UDF_INFO_REMAP
|
|
|
|
typedef struct _UDF_VERIFY_ITEM {
|
|
lba_t lba;
|
|
ULONG crc;
|
|
PUCHAR Buffer;
|
|
LIST_ENTRY vrfList;
|
|
BOOLEAN queued;
|
|
} UDF_VERIFY_ITEM, *PUDF_VERIFY_ITEM;
|
|
|
|
typedef struct _UDF_VERIFY_REQ_RANGE {
|
|
lba_t lba;
|
|
uint32 BCount;
|
|
} UDF_VERIFY_REQ_RANGE, *PUDF_VERIFY_REQ_RANGE;
|
|
|
|
#define MAX_VREQ_RANGES 128
|
|
|
|
typedef struct _UDF_VERIFY_REQ {
|
|
PVCB Vcb;
|
|
PUCHAR Buffer;
|
|
ULONG nReq;
|
|
UDF_VERIFY_REQ_RANGE vr[MAX_VREQ_RANGES];
|
|
#ifndef _CONSOLE
|
|
WORK_QUEUE_ITEM VerifyItem;
|
|
#endif
|
|
} UDF_VERIFY_REQ, *PUDF_VERIFY_REQ;
|
|
|
|
VOID
|
|
UDFVRemoveBlock(
|
|
PUDF_VERIFY_CTX VerifyCtx,
|
|
PUDF_VERIFY_ITEM vItem
|
|
);
|
|
|
|
OSSTATUS
|
|
UDFVInit(
|
|
IN PVCB Vcb
|
|
)
|
|
{
|
|
PUDF_VERIFY_CTX VerifyCtx = &Vcb->VerifyCtx;
|
|
uint32 i;
|
|
OSSTATUS status = STATUS_SUCCESS;
|
|
BOOLEAN res_inited = FALSE;
|
|
|
|
if(VerifyCtx->VInited) {
|
|
UDFPrint(("Already inited\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
_SEH2_TRY {
|
|
RtlZeroMemory(VerifyCtx, sizeof(UDF_VERIFY_CTX));
|
|
if(!Vcb->VerifyOnWrite) {
|
|
UDFPrint(("Verify is disabled\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
if(Vcb->CDR_Mode) {
|
|
UDFPrint(("Verify is not intended for CD/DVD-R\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
if(!OS_SUCCESS(status = ExInitializeResourceLite(&(VerifyCtx->VerifyLock)))) {
|
|
try_return(status);
|
|
}
|
|
res_inited = TRUE;
|
|
VerifyCtx->ItemCount = 0;
|
|
VerifyCtx->StoredBitMap = (uint8*)DbgAllocatePoolWithTag(PagedPool, (i = (Vcb->LastPossibleLBA+1+7)>>3), 'mNWD' );
|
|
if(VerifyCtx->StoredBitMap) {
|
|
RtlZeroMemory(VerifyCtx->StoredBitMap, i);
|
|
} else {
|
|
UDFPrint(("Can't alloc verify bitmap for %x blocks\n", Vcb->LastPossibleLBA));
|
|
try_return(status = STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
InitializeListHead(&(VerifyCtx->vrfList));
|
|
KeInitializeEvent(&(VerifyCtx->vrfEvent), SynchronizationEvent, FALSE);
|
|
VerifyCtx->WaiterCount = 0;
|
|
VerifyCtx->VInited = TRUE;
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
if(!OS_SUCCESS(status)) {
|
|
if(res_inited) {
|
|
ExDeleteResourceLite(&(VerifyCtx->VerifyLock));
|
|
}
|
|
}
|
|
} _SEH2_END;
|
|
return status;
|
|
} // end UDFVInit()
|
|
|
|
VOID
|
|
UDFVWaitQueued(
|
|
PUDF_VERIFY_CTX VerifyCtx
|
|
)
|
|
{
|
|
ULONG w;
|
|
|
|
while(VerifyCtx->QueuedCount) {
|
|
UDFPrint(("UDFVWaitQueued: wait for completion (%d)\n", VerifyCtx->QueuedCount));
|
|
w = InterlockedIncrement((PLONG)&(VerifyCtx->WaiterCount));
|
|
UDFPrint((" %d waiters\n", w));
|
|
DbgWaitForSingleObject(&(VerifyCtx->vrfEvent), NULL);
|
|
if((w = InterlockedDecrement((PLONG)&(VerifyCtx->WaiterCount)))) {
|
|
UDFPrint((" still %d waiters, q %d\n", w, VerifyCtx->QueuedCount));
|
|
if(!VerifyCtx->QueuedCount) {
|
|
UDFPrint((" pulse event\n", w));
|
|
KeSetEvent(&(VerifyCtx->vrfEvent), 0, FALSE);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
} // end UDFVWaitQueued()
|
|
|
|
VOID
|
|
UDFVRelease(
|
|
IN PVCB Vcb
|
|
)
|
|
{
|
|
PUDF_VERIFY_CTX VerifyCtx = &Vcb->VerifyCtx;
|
|
PLIST_ENTRY Link;
|
|
PUDF_VERIFY_ITEM vItem;
|
|
|
|
if(!VerifyCtx->VInited) {
|
|
return;
|
|
}
|
|
|
|
UDFPrint(("UDFVRelease: wait for completion\n"));
|
|
UDFVWaitQueued(VerifyCtx);
|
|
|
|
UDFAcquireResourceExclusive(&(VerifyCtx->VerifyLock), TRUE);
|
|
|
|
Link = VerifyCtx->vrfList.Flink;
|
|
|
|
while(Link != &(VerifyCtx->vrfList)) {
|
|
vItem = CONTAINING_RECORD( Link, UDF_VERIFY_ITEM, vrfList );
|
|
Link = Link->Flink;
|
|
//DbgFreePool(vItem);
|
|
UDFVRemoveBlock(VerifyCtx, vItem);
|
|
}
|
|
VerifyCtx->VInited = FALSE;
|
|
|
|
UDFReleaseResource(&(VerifyCtx->VerifyLock));
|
|
|
|
ExDeleteResourceLite(&(VerifyCtx->VerifyLock));
|
|
DbgFreePool(VerifyCtx->StoredBitMap);
|
|
|
|
RtlZeroMemory(VerifyCtx, sizeof(UDF_VERIFY_CTX));
|
|
|
|
return;
|
|
} // end UDFVRelease()
|
|
|
|
PUDF_VERIFY_ITEM
|
|
UDFVStoreBlock(
|
|
IN PVCB Vcb,
|
|
IN uint32 LBA,
|
|
IN PVOID Buffer,
|
|
PLIST_ENTRY Link
|
|
)
|
|
{
|
|
PUDF_VERIFY_CTX VerifyCtx = &Vcb->VerifyCtx;
|
|
PUDF_VERIFY_ITEM vItem;
|
|
|
|
UDFPrint(("v-add %x\n", LBA));
|
|
|
|
vItem = (PUDF_VERIFY_ITEM)DbgAllocatePoolWithTag(PagedPool, sizeof(UDF_VERIFY_ITEM)+Vcb->BlockSize, 'bvWD');
|
|
if(!vItem)
|
|
return NULL;
|
|
RtlCopyMemory(vItem+1, Buffer, Vcb->BlockSize);
|
|
vItem->lba = LBA;
|
|
vItem->crc = crc32((PUCHAR)Buffer, Vcb->BlockSize);
|
|
vItem->Buffer = (PUCHAR)(vItem+1);
|
|
vItem->queued = FALSE;
|
|
InitializeListHead(&(vItem->vrfList));
|
|
InsertTailList(Link, &(vItem->vrfList));
|
|
UDFSetBit(VerifyCtx->StoredBitMap, LBA);
|
|
VerifyCtx->ItemCount++;
|
|
return vItem;
|
|
} // end UDFVStoreBlock()
|
|
|
|
VOID
|
|
UDFVUpdateBlock(
|
|
IN PVCB Vcb,
|
|
IN PVOID Buffer,
|
|
PUDF_VERIFY_ITEM vItem
|
|
)
|
|
{
|
|
UDFPrint(("v-upd %x\n", vItem->lba));
|
|
RtlCopyMemory(vItem+1, Buffer, Vcb->BlockSize);
|
|
vItem->crc = crc32((PUCHAR)Buffer, Vcb->BlockSize);
|
|
return;
|
|
} // end UDFVUpdateBlock()
|
|
|
|
VOID
|
|
UDFVRemoveBlock(
|
|
PUDF_VERIFY_CTX VerifyCtx,
|
|
PUDF_VERIFY_ITEM vItem
|
|
)
|
|
{
|
|
UDFPrint(("v-del %x\n", vItem->lba));
|
|
UDFClrBit(VerifyCtx->StoredBitMap, vItem->lba);
|
|
RemoveEntryList(&(vItem->vrfList));
|
|
VerifyCtx->ItemCount--;
|
|
DbgFreePool(vItem);
|
|
return;
|
|
} // end UDFVUpdateBlock()
|
|
|
|
OSSTATUS
|
|
UDFVWrite(
|
|
IN PVCB Vcb,
|
|
IN void* Buffer, // Target buffer
|
|
IN uint32 BCount,
|
|
IN uint32 LBA,
|
|
// OUT PSIZE_T WrittenBytes,
|
|
IN uint32 Flags
|
|
)
|
|
{
|
|
PLIST_ENTRY Link;
|
|
PUDF_VERIFY_ITEM vItem;
|
|
//PUDF_VERIFY_ITEM vItem1;
|
|
PUDF_VERIFY_CTX VerifyCtx = &Vcb->VerifyCtx;
|
|
ULONG i;
|
|
ULONG n;
|
|
//uint32 prev_lba;
|
|
|
|
if(!VerifyCtx->VInited) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
UDFAcquireResourceExclusive(&(VerifyCtx->VerifyLock), TRUE);
|
|
|
|
for(i=0, n=0; i<BCount; i++) {
|
|
if(UDFGetBit(VerifyCtx->StoredBitMap, LBA+i)) {
|
|
// some blocks are remembered
|
|
n++;
|
|
}
|
|
}
|
|
|
|
if(n == BCount) {
|
|
// update all blocks
|
|
n = 0;
|
|
Link = VerifyCtx->vrfList.Blink;
|
|
while(Link != &(VerifyCtx->vrfList)) {
|
|
vItem = CONTAINING_RECORD( Link, UDF_VERIFY_ITEM, vrfList );
|
|
Link = Link->Blink;
|
|
if(vItem->lba >= LBA && vItem->lba < LBA+BCount) {
|
|
ASSERT(UDFGetBit(VerifyCtx->StoredBitMap, vItem->lba));
|
|
UDFVUpdateBlock(Vcb, ((PUCHAR)Buffer)+(vItem->lba-LBA)*Vcb->BlockSize, vItem);
|
|
n++;
|
|
if(n == BCount) {
|
|
// all updated
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
if(n) {
|
|
#if 0
|
|
// find remembered blocks (the 1st one)
|
|
Link = VerifyCtx->vrfList.Blink;
|
|
while(Link != &(VerifyCtx->vrfList)) {
|
|
vItem = CONTAINING_RECORD( Link, UDF_VERIFY_ITEM, vrfList );
|
|
Link = Link->Blink;
|
|
if(vItem->lba >= LBA && vItem->lba < LBA+BCount) {
|
|
//UDFVRemoveBlock(VerifyCtx, vItem);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check if contiguous
|
|
i=1;
|
|
prev_lba = vItem->lba;
|
|
vItem1 = vItem;
|
|
Link = Link->Blink;
|
|
while((i < n) && (Link != &(VerifyCtx->vrfList))) {
|
|
vItem = CONTAINING_RECORD( Link, UDF_VERIFY_ITEM, vrfList );
|
|
Link = Link->Blink;
|
|
if(vItem->lba > LBA || vItem->lba >= LBA+BCount) {
|
|
// end
|
|
break;
|
|
}
|
|
if(vItem->lba < prev_lba) {
|
|
// not sorted
|
|
break;
|
|
}
|
|
prev_lba = vItem->lba;
|
|
i++;
|
|
}
|
|
|
|
if(i == n) {
|
|
// cont
|
|
} else {
|
|
// drop all and add again
|
|
}
|
|
|
|
vItem1 = vItem;
|
|
for(i=0; i<BCount; i++) {
|
|
if(vItem->lba == LBA+i) {
|
|
ASSERT(UDFGetBit(VerifyCtx->StoredBitMap, LBA+i));
|
|
UDFVUpdateBlock(Vcb, ((PUCHAR)Buffer)+i*Vcb->BlockSize, vItem);
|
|
continue;
|
|
}
|
|
if(vItem1->lba == LBA+i) {
|
|
ASSERT(UDFGetBit(VerifyCtx->StoredBitMap, LBA+i));
|
|
UDFVUpdateBlock(Vcb, ((PUCHAR)Buffer)+i*Vcb->BlockSize, vItem1);
|
|
continue;
|
|
}
|
|
if(vItem1->lba > LBA+i) {
|
|
// just insert this block
|
|
ASSERT(!UDFGetBit(VerifyCtx->StoredBitMap, LBA+i));
|
|
UDFVStoreBlock(Vcb, LBA+i, ((PUCHAR)Buffer)+i*Vcb->BlockSize, &(vItem1->vrfList));
|
|
} else {
|
|
vItem = CONTAINING_RECORD( vItem->vrfList.Blink, UDF_VERIFY_ITEM, vrfList );
|
|
}
|
|
}
|
|
#else
|
|
Link = VerifyCtx->vrfList.Blink;
|
|
i=0;
|
|
while(Link != &(VerifyCtx->vrfList)) {
|
|
vItem = CONTAINING_RECORD( Link, UDF_VERIFY_ITEM, vrfList );
|
|
Link = Link->Blink;
|
|
if(vItem->lba >= LBA && vItem->lba < LBA+BCount) {
|
|
UDFVRemoveBlock(VerifyCtx, vItem);
|
|
i++;
|
|
if(i == n) {
|
|
// all killed
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
goto remember_all;
|
|
#endif
|
|
|
|
} else {
|
|
remember_all:
|
|
// remember all blocks
|
|
for(i=0; i<BCount; i++) {
|
|
ASSERT(!UDFGetBit(VerifyCtx->StoredBitMap, LBA+i));
|
|
UDFVStoreBlock(Vcb, LBA+i, ((PUCHAR)Buffer)+i*Vcb->BlockSize, &(VerifyCtx->vrfList));
|
|
}
|
|
}
|
|
|
|
if(VerifyCtx->ItemCount > UDF_MAX_VERIFY_CACHE) {
|
|
UDFVVerify(Vcb, UFD_VERIFY_FLAG_LOCKED);
|
|
}
|
|
|
|
UDFReleaseResource(&(VerifyCtx->VerifyLock));
|
|
|
|
if(VerifyCtx->ItemCount > UDF_MAX_VERIFY_CACHE*2) {
|
|
//UDFVVerify(Vcb, UFD_VERIFY_FLAG_LOCKED);
|
|
// TODO: make some delay
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // end UDFVWrite()
|
|
|
|
OSSTATUS
|
|
UDFVRead(
|
|
IN PVCB Vcb,
|
|
IN void* Buffer, // Target buffer
|
|
IN uint32 BCount,
|
|
IN uint32 LBA,
|
|
// OUT uint32* ReadBytes,
|
|
IN uint32 Flags
|
|
)
|
|
{
|
|
PLIST_ENTRY Link;
|
|
PUDF_VERIFY_ITEM vItem;
|
|
PUDF_VERIFY_CTX VerifyCtx = &Vcb->VerifyCtx;
|
|
ULONG crc;
|
|
ULONG i;
|
|
ULONG n;
|
|
OSSTATUS status = STATUS_SUCCESS;
|
|
uint32* bm;
|
|
|
|
if(!VerifyCtx->VInited) {
|
|
return STATUS_SUCCESS;
|
|
//return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
UDFAcquireResourceExclusive(&(VerifyCtx->VerifyLock), TRUE);
|
|
|
|
for(i=0, n=0; i<BCount; i++) {
|
|
if(UDFGetBit(VerifyCtx->StoredBitMap, LBA+i)) {
|
|
// some blocks are remembered
|
|
n++;
|
|
}
|
|
}
|
|
|
|
if(!n) {
|
|
// no blocks are remembered
|
|
UDFReleaseResource(&(VerifyCtx->VerifyLock));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Link = VerifyCtx->vrfList.Flink;
|
|
i=0;
|
|
while(Link != &(VerifyCtx->vrfList)) {
|
|
vItem = CONTAINING_RECORD( Link, UDF_VERIFY_ITEM, vrfList );
|
|
Link = Link->Flink;
|
|
if(vItem->lba >= LBA && vItem->lba < LBA+BCount) {
|
|
ASSERT(UDFGetBit(VerifyCtx->StoredBitMap, vItem->lba));
|
|
i++;
|
|
if(!(Flags & PH_READ_VERIFY_CACHE)) {
|
|
crc = crc32((PUCHAR)Buffer+(vItem->lba - LBA)*Vcb->BlockSize, Vcb->BlockSize);
|
|
if(vItem->crc != crc) {
|
|
UDFPrint(("UDFVRead: stored %x != %x\n", vItem->crc, crc));
|
|
RtlCopyMemory((PUCHAR)Buffer+(vItem->lba - LBA)*Vcb->BlockSize, vItem->Buffer, Vcb->BlockSize);
|
|
status = STATUS_FT_WRITE_RECOVERY;
|
|
|
|
if(!(bm = (uint32*)(Vcb->BSBM_Bitmap))) {
|
|
crc = (Vcb->LastPossibleLBA+1+7) >> 3; // reuse 'crc' variable
|
|
bm = (uint32*)(Vcb->BSBM_Bitmap = (int8*)DbgAllocatePoolWithTag(NonPagedPool, crc, 'mNWD' ));
|
|
if(bm) {
|
|
RtlZeroMemory(bm, crc);
|
|
} else {
|
|
UDFPrint(("Can't alloc BSBM for %x blocks\n", Vcb->LastPossibleLBA));
|
|
}
|
|
}
|
|
if(bm) {
|
|
UDFSetBit(bm, vItem->lba);
|
|
UDFPrint(("Set BB @ %#x\n", vItem->lba));
|
|
}
|
|
#ifdef _BROWSE_UDF_
|
|
bm = (uint32*)(Vcb->FSBM_Bitmap);
|
|
if(bm) {
|
|
UDFSetUsedBit(bm, vItem->lba);
|
|
UDFPrint(("Set BB @ %#x as used\n", vItem->lba));
|
|
}
|
|
#endif //_BROWSE_UDF_
|
|
} else {
|
|
// ok
|
|
}
|
|
} else {
|
|
UDFPrint(("UDFVRead: get cached @ %x\n", vItem->lba));
|
|
RtlCopyMemory((PUCHAR)Buffer+(vItem->lba - LBA)*Vcb->BlockSize, vItem->Buffer, Vcb->BlockSize);
|
|
}
|
|
if(i >= n) {
|
|
// no more blocks expected
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if((status == STATUS_SUCCESS && !(Flags & PH_KEEP_VERIFY_CACHE)) || (Flags & PH_FORGET_VERIFIED)) {
|
|
// ok, forget this, no errors found
|
|
Link = VerifyCtx->vrfList.Flink;
|
|
i = 0;
|
|
while(Link != &(VerifyCtx->vrfList)) {
|
|
vItem = CONTAINING_RECORD( Link, UDF_VERIFY_ITEM, vrfList );
|
|
Link = Link->Flink;
|
|
if(vItem->lba >= LBA && vItem->lba < LBA+BCount) {
|
|
i++;
|
|
UDFVRemoveBlock(VerifyCtx, vItem);
|
|
if(i >= n) {
|
|
// no more blocks expected
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UDFReleaseResource(&(VerifyCtx->VerifyLock));
|
|
return status;
|
|
|
|
} // end UDFVRead()
|
|
|
|
OSSTATUS
|
|
UDFVForget(
|
|
IN PVCB Vcb,
|
|
IN uint32 BCount,
|
|
IN uint32 LBA,
|
|
IN uint32 Flags
|
|
)
|
|
{
|
|
PLIST_ENTRY Link;
|
|
PUDF_VERIFY_ITEM vItem;
|
|
PUDF_VERIFY_CTX VerifyCtx = &Vcb->VerifyCtx;
|
|
ULONG i;
|
|
ULONG n;
|
|
OSSTATUS status = STATUS_SUCCESS;
|
|
|
|
if(!VerifyCtx->VInited) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
UDFAcquireResourceExclusive(&(VerifyCtx->VerifyLock), TRUE);
|
|
|
|
for(i=0, n=0; i<BCount; i++) {
|
|
if(UDFGetBit(VerifyCtx->StoredBitMap, LBA+i)) {
|
|
// some blocks are remembered
|
|
n++;
|
|
}
|
|
}
|
|
|
|
if(!n) {
|
|
// no blocks are remembered
|
|
UDFReleaseResource(&(VerifyCtx->VerifyLock));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Link = VerifyCtx->vrfList.Flink;
|
|
i = 0;
|
|
while(Link != &(VerifyCtx->vrfList)) {
|
|
vItem = CONTAINING_RECORD( Link, UDF_VERIFY_ITEM, vrfList );
|
|
Link = Link->Flink;
|
|
if(vItem->lba >= LBA && vItem->lba < LBA+BCount) {
|
|
i++;
|
|
UDFVRemoveBlock(VerifyCtx, vItem);
|
|
if(i >= n) {
|
|
// no more blocks expected
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
UDFReleaseResource(&(VerifyCtx->VerifyLock));
|
|
return status;
|
|
|
|
} // end UDFVForget()
|
|
|
|
VOID
|
|
NTAPI
|
|
UDFVWorkItem(
|
|
PVOID Context
|
|
)
|
|
{
|
|
PUDF_VERIFY_REQ VerifyReq = (PUDF_VERIFY_REQ)Context;
|
|
PVCB Vcb = VerifyReq->Vcb;
|
|
SIZE_T ReadBytes;
|
|
// OSSTATUS RC;
|
|
ULONG i;
|
|
|
|
ReadBytes = (SIZE_T)Vcb;
|
|
#if 1
|
|
if(Vcb->SparingCountFree) {
|
|
WCacheStartDirect__(&(Vcb->FastCache), Vcb, TRUE);
|
|
for(i=0; i<VerifyReq->nReq; i++) {
|
|
UDFTIOVerify(Vcb,
|
|
VerifyReq->Buffer, // Target buffer
|
|
VerifyReq->vr[i].BCount << Vcb->BlockSizeBits,
|
|
VerifyReq->vr[i].lba,
|
|
&ReadBytes,
|
|
PH_TMP_BUFFER | PH_VCB_IN_RETLEN /*| PH_LOCK_CACHE*/);
|
|
}
|
|
WCacheEODirect__(&(Vcb->FastCache), Vcb);
|
|
} else {
|
|
for(i=0; i<VerifyReq->nReq; i++) {
|
|
UDFPrint(("!!! No more space for remap !!!\n"));
|
|
UDFPrint((" try del from verify cache @ %x\n", VerifyReq->vr[i].lba));
|
|
UDFVRead(Vcb, VerifyReq->Buffer, VerifyReq->vr[i].BCount, VerifyReq->vr[i].lba,
|
|
PH_FORGET_VERIFIED | PH_READ_VERIFY_CACHE | PH_TMP_BUFFER);
|
|
}
|
|
}
|
|
#else
|
|
for(i=0; i<VerifyReq->nReq; i++) {
|
|
if(Vcb->SparingCountFree) {
|
|
WCacheStartDirect__(&(Vcb->FastCache), Vcb, TRUE);
|
|
RC = UDFTIOVerify(Vcb,
|
|
VerifyReq->Buffer, // Target buffer
|
|
VerifyReq->vr[i].BCount << Vcb->BlockSizeBits,
|
|
VerifyReq->vr[i].lba,
|
|
&ReadBytes,
|
|
PH_TMP_BUFFER | PH_VCB_IN_RETLEN /*| PH_LOCK_CACHE*/);
|
|
WCacheEODirect__(&(Vcb->FastCache), Vcb);
|
|
} else {
|
|
UDFPrint(("!!! No more space for remap !!!\n"));
|
|
UDFPrint((" try del from verify cache @ %x\n", VerifyReq->vr[i].lba));
|
|
RC = UDFVRead(Vcb, VerifyReq->Buffer, VerifyReq->vr[i].BCount, VerifyReq->vr[i].lba,
|
|
PH_FORGET_VERIFIED | PH_READ_VERIFY_CACHE | PH_TMP_BUFFER);
|
|
}
|
|
}
|
|
#endif
|
|
DbgFreePool(VerifyReq->Buffer);
|
|
DbgFreePool(VerifyReq);
|
|
InterlockedDecrement((PLONG)&(Vcb->VerifyCtx.QueuedCount));
|
|
UDFPrint((" QueuedCount = %d\n", Vcb->VerifyCtx.QueuedCount));
|
|
UDFPrint((" Setting event...\n"));
|
|
KeSetEvent(&(Vcb->VerifyCtx.vrfEvent), 0, FALSE);
|
|
return;
|
|
} // end UDFVWorkItem()
|
|
|
|
VOID
|
|
UDFVVerify(
|
|
IN PVCB Vcb,
|
|
IN ULONG Flags
|
|
)
|
|
{
|
|
PUDF_VERIFY_CTX VerifyCtx = &Vcb->VerifyCtx;
|
|
PLIST_ENTRY Link;
|
|
PUDF_VERIFY_ITEM vItem;
|
|
PUDF_VERIFY_REQ VerifyReq = NULL;
|
|
ULONG len, max_len=0;
|
|
lba_t prev_lba;
|
|
//PUCHAR tmp_buff;
|
|
ULONG i;
|
|
BOOLEAN do_vrf = FALSE;
|
|
|
|
if(!VerifyCtx->VInited) {
|
|
return;
|
|
}
|
|
if(VerifyCtx->QueuedCount) {
|
|
if(Flags & UFD_VERIFY_FLAG_WAIT) {
|
|
UDFPrint((" wait for verify flush\n"));
|
|
goto wait;
|
|
}
|
|
UDFPrint((" verify flush already queued\n"));
|
|
return;
|
|
}
|
|
|
|
if(!(Flags & (UFD_VERIFY_FLAG_FORCE | UFD_VERIFY_FLAG_BG))) {
|
|
if(VerifyCtx->ItemCount < UDF_MAX_VERIFY_CACHE) {
|
|
return;
|
|
}
|
|
|
|
}
|
|
if(!(Flags & UFD_VERIFY_FLAG_LOCKED)) {
|
|
UDFAcquireResourceExclusive(&(VerifyCtx->VerifyLock), TRUE);
|
|
}
|
|
|
|
if(Flags & UFD_VERIFY_FLAG_FORCE) {
|
|
i = VerifyCtx->ItemCount;
|
|
} else {
|
|
if(VerifyCtx->ItemCount >= UDF_MAX_VERIFY_CACHE) {
|
|
i = VerifyCtx->ItemCount - UDF_VERIFY_CACHE_LOW;
|
|
} else {
|
|
i = min(UDF_VERIFY_CACHE_GRAN, VerifyCtx->ItemCount);
|
|
}
|
|
}
|
|
|
|
Link = VerifyCtx->vrfList.Flink;
|
|
prev_lba = -2;
|
|
len = 0;
|
|
|
|
while(i) {
|
|
ASSERT(Link != &(VerifyCtx->vrfList));
|
|
/*
|
|
if(Link == &(VerifyCtx->vrfList)) {
|
|
if(!len)
|
|
break;
|
|
i=1;
|
|
goto queue_req;
|
|
}
|
|
*/
|
|
vItem = CONTAINING_RECORD( Link, UDF_VERIFY_ITEM, vrfList );
|
|
Link = Link->Flink;
|
|
|
|
//
|
|
if(!vItem->queued && (prev_lba+len == vItem->lba)) {
|
|
vItem->queued = TRUE;
|
|
len++;
|
|
} else {
|
|
if(len) {
|
|
do_vrf = TRUE;
|
|
} else {
|
|
len = 1;
|
|
prev_lba = vItem->lba;
|
|
}
|
|
}
|
|
if((i == 1) && len) {
|
|
do_vrf = TRUE;
|
|
}
|
|
if(len >= 0x100) {
|
|
do_vrf = TRUE;
|
|
}
|
|
if(do_vrf) {
|
|
//queue_req:
|
|
if(!VerifyReq) {
|
|
VerifyReq = (PUDF_VERIFY_REQ)DbgAllocatePoolWithTag(NonPagedPool, sizeof(UDF_VERIFY_REQ), 'bNWD');
|
|
if(VerifyReq) {
|
|
RtlZeroMemory(VerifyReq, sizeof(UDF_VERIFY_REQ));
|
|
VerifyReq->Vcb = Vcb;
|
|
}
|
|
}
|
|
if(VerifyReq) {
|
|
|
|
VerifyReq->vr[VerifyReq->nReq].lba = prev_lba;
|
|
VerifyReq->vr[VerifyReq->nReq].BCount = len;
|
|
VerifyReq->nReq++;
|
|
if(max_len < len) {
|
|
max_len = len;
|
|
}
|
|
|
|
if((VerifyReq->nReq >= MAX_VREQ_RANGES) || (i == 1)) {
|
|
|
|
VerifyReq->Buffer = (PUCHAR)DbgAllocatePoolWithTag(NonPagedPool, max_len * Vcb->BlockSize, 'bNWD');
|
|
if(VerifyReq->Buffer) {
|
|
InterlockedIncrement((PLONG)&(VerifyCtx->QueuedCount));
|
|
#ifndef _CONSOLE
|
|
ExInitializeWorkItem( &(VerifyReq->VerifyItem),
|
|
UDFVWorkItem,
|
|
VerifyReq );
|
|
ExQueueWorkItem( &(VerifyReq->VerifyItem), CriticalWorkQueue );
|
|
#else
|
|
UDFVWorkItem(VerifyReq);
|
|
#endif
|
|
} else {
|
|
DbgFreePool(VerifyReq);
|
|
}
|
|
VerifyReq = NULL;
|
|
max_len = 0;
|
|
} else {
|
|
}
|
|
}
|
|
len = 1;
|
|
prev_lba = vItem->lba;
|
|
do_vrf = FALSE;
|
|
}
|
|
i--;
|
|
}
|
|
|
|
if(!(Flags & UFD_VERIFY_FLAG_LOCKED)) {
|
|
UDFReleaseResource(&(VerifyCtx->VerifyLock));
|
|
}
|
|
if(Flags & UFD_VERIFY_FLAG_WAIT) {
|
|
wait:
|
|
UDFPrint(("UDFVVerify: wait for completion\n"));
|
|
UDFVWaitQueued(VerifyCtx);
|
|
}
|
|
|
|
return;
|
|
} // end UDFVVerify()
|
|
|
|
VOID
|
|
UDFVFlush(
|
|
IN PVCB Vcb
|
|
)
|
|
{
|
|
PUDF_VERIFY_CTX VerifyCtx = &Vcb->VerifyCtx;
|
|
|
|
if(!VerifyCtx->VInited) {
|
|
return;
|
|
}
|
|
|
|
UDFPrint(("UDFVFlush: wait for completion\n"));
|
|
UDFVWaitQueued(VerifyCtx);
|
|
|
|
UDFVVerify(Vcb, UFD_VERIFY_FLAG_FORCE);
|
|
|
|
UDFPrint(("UDFVFlush: wait for completion (2)\n"));
|
|
UDFVWaitQueued(VerifyCtx);
|
|
} // end UDFVFlush()
|
|
|
|
BOOLEAN
|
|
__fastcall
|
|
UDFCheckArea(
|
|
IN PVCB Vcb,
|
|
IN lba_t LBA,
|
|
IN uint32 BCount
|
|
)
|
|
{
|
|
uint8* buff;
|
|
OSSTATUS RC;
|
|
SIZE_T ReadBytes;
|
|
uint32 i, d;
|
|
BOOLEAN ext_ok = TRUE;
|
|
EXTENT_MAP Map[2];
|
|
uint32 PS = Vcb->WriteBlockSize >> Vcb->BlockSizeBits;
|
|
|
|
buff = (uint8*)DbgAllocatePoolWithTag(NonPagedPool, Vcb->WriteBlockSize, 'bNWD' );
|
|
if(buff) {
|
|
for(i=0; i<BCount; i+=d) {
|
|
if(!((LBA+i) & (PS-1)) &&
|
|
(i+PS <= BCount)) {
|
|
d = PS;
|
|
} else {
|
|
d = 1;
|
|
}
|
|
RC = UDFTRead(Vcb,
|
|
buff,
|
|
d << Vcb->BlockSizeBits,
|
|
LBA+i,
|
|
&ReadBytes,
|
|
PH_TMP_BUFFER);
|
|
|
|
if(RC != STATUS_SUCCESS) {
|
|
Map[0].extLocation = LBA+i;
|
|
Map[0].extLength = d << Vcb->BlockSizeBits;
|
|
UDFMarkSpaceAsXXXNoProtect(Vcb, 0, &(Map[0]), AS_DISCARDED | AS_BAD); // free
|
|
ext_ok = FALSE;
|
|
}
|
|
}
|
|
DbgFreePool(buff);
|
|
}
|
|
return ext_ok;
|
|
} // end UDFCheckArea()
|
|
|
|
/*
|
|
This routine remaps sectors from bad packet
|
|
*/
|
|
OSSTATUS
|
|
__fastcall
|
|
UDFRemapPacket(
|
|
IN PVCB Vcb,
|
|
IN uint32 Lba,
|
|
IN BOOLEAN RemapSpared
|
|
)
|
|
{
|
|
uint32 i, max, BS, orig;
|
|
PSPARING_MAP Map;
|
|
BOOLEAN verified = FALSE;
|
|
|
|
if(Vcb->SparingTable) {
|
|
|
|
max = Vcb->SparingCount;
|
|
BS = Vcb->SparingBlockSize;
|
|
|
|
// use sparing table for relocation
|
|
if(Vcb->SparingCountFree == (ULONG)-1) {
|
|
UDFPrint(("calculate free spare areas\n"));
|
|
re_check:
|
|
UDFPrint(("verify spare area\n"));
|
|
Vcb->SparingCountFree = 0;
|
|
Map = Vcb->SparingTable;
|
|
for(i=0;i<max;i++,Map++) {
|
|
if(Map->origLocation == SPARING_LOC_AVAILABLE) {
|
|
if(UDFCheckArea(Vcb, Map->mappedLocation, BS)) {
|
|
Vcb->SparingCountFree++;
|
|
} else {
|
|
UDFPrint(("initial check: bad spare block @ %x\n", Map->mappedLocation));
|
|
Map->origLocation = SPARING_LOC_CORRUPTED;
|
|
Vcb->SparingTableModified = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!Vcb->SparingCountFree) {
|
|
UDFPrint(("sparing table full\n"));
|
|
return STATUS_DISK_FULL;
|
|
}
|
|
|
|
Map = Vcb->SparingTable;
|
|
Lba &= ~(BS-1);
|
|
for(i=0;i<max;i++,Map++) {
|
|
orig = Map->origLocation;
|
|
if(Lba == (orig & ~(BS-1)) ) {
|
|
// already remapped
|
|
|
|
UDFPrint(("remap remapped: bad spare block @ %x\n", Map->mappedLocation));
|
|
if(!verified) {
|
|
verified = TRUE;
|
|
goto re_check;
|
|
}
|
|
|
|
if(!RemapSpared) {
|
|
return STATUS_SHARING_VIOLATION;
|
|
} else {
|
|
// look for another remap area
|
|
Map->origLocation = SPARING_LOC_CORRUPTED;
|
|
Vcb->SparingTableModified = TRUE;
|
|
Vcb->SparingCountFree--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Map = Vcb->SparingTable;
|
|
for(i=0;i<max;i++,Map++) {
|
|
if(Map->origLocation == SPARING_LOC_AVAILABLE) {
|
|
UDFPrint(("remap %x -> %x\n", Lba, Map->mappedLocation));
|
|
Map->origLocation = Lba;
|
|
Vcb->SparingTableModified = TRUE;
|
|
Vcb->SparingCountFree--;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
UDFPrint(("sparing table full\n"));
|
|
return STATUS_DISK_FULL;
|
|
}
|
|
return STATUS_UNSUCCESSFUL;
|
|
} // end UDFRemapPacket()
|
|
|
|
/*
|
|
This routine releases sector mapping when entire packet is marked as free
|
|
*/
|
|
OSSTATUS
|
|
__fastcall
|
|
UDFUnmapRange(
|
|
IN PVCB Vcb,
|
|
IN uint32 Lba,
|
|
IN uint32 BCount
|
|
)
|
|
{
|
|
uint32 i, max, BS, orig;
|
|
PSPARING_MAP Map;
|
|
|
|
if(Vcb->SparingTable) {
|
|
// use sparing table for relocation
|
|
|
|
max = Vcb->SparingCount;
|
|
BS = Vcb->SparingBlockSize;
|
|
Map = Vcb->SparingTable;
|
|
for(i=0;i<max;i++,Map++) {
|
|
orig = Map->origLocation;
|
|
switch(orig) {
|
|
case SPARING_LOC_AVAILABLE:
|
|
case SPARING_LOC_CORRUPTED:
|
|
continue;
|
|
}
|
|
if(orig >= Lba &&
|
|
(orig+BS) <= (Lba+BCount)) {
|
|
// unmap
|
|
UDFPrint(("unmap %x -> %x\n", orig, Map->mappedLocation));
|
|
Map->origLocation = SPARING_LOC_AVAILABLE;
|
|
Vcb->SparingTableModified = TRUE;
|
|
Vcb->SparingCountFree++;
|
|
}
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
} // end UDFUnmapRange()
|
|
|
|
/*
|
|
This routine returns physical address for relocated sector
|
|
*/
|
|
uint32
|
|
__fastcall
|
|
UDFRelocateSector(
|
|
IN PVCB Vcb,
|
|
IN uint32 Lba
|
|
)
|
|
{
|
|
uint32 i, max, BS, orig;
|
|
|
|
if(Vcb->SparingTable) {
|
|
// use sparing table for relocation
|
|
uint32 _Lba;
|
|
PSPARING_MAP Map = Vcb->SparingTable;
|
|
|
|
max = Vcb->SparingCount;
|
|
BS = Vcb->SparingBlockSize;
|
|
_Lba = Lba & ~(BS-1);
|
|
for(i=0;i<max;i++,Map++) {
|
|
orig = Map->origLocation;
|
|
if(_Lba == (orig & ~(BS-1)) ) {
|
|
//if( (Lba >= (orig = Map->origLocation)) && (Lba < orig + BS) ) {
|
|
return Map->mappedLocation + Lba - orig;
|
|
}
|
|
}
|
|
} else if(Vcb->Vat) {
|
|
// use VAT for relocation
|
|
uint32* Map = Vcb->Vat;
|
|
uint32 root;
|
|
// check if given Lba lays in the partition covered by VAT
|
|
if(Lba >= Vcb->NWA)
|
|
return Vcb->NWA;
|
|
if(Lba < (root = Vcb->Partitions[Vcb->VatPartNdx].PartitionRoot))
|
|
return Lba;
|
|
Map = &(Vcb->Vat[(i = Lba - root)]);
|
|
if((i < Vcb->VatCount) && (i=(*Map)) ) {
|
|
if(i != UDF_VAT_FREE_ENTRY) {
|
|
return i + root;
|
|
} else {
|
|
return 0x7fffffff;
|
|
}
|
|
}
|
|
}
|
|
return Lba;
|
|
} // end UDFRelocateSector()
|
|
|
|
/*
|
|
This routine checks if the extent specified requires relocation
|
|
*/
|
|
BOOLEAN
|
|
__fastcall
|
|
UDFAreSectorsRelocated(
|
|
IN PVCB Vcb,
|
|
IN uint32 Lba,
|
|
IN uint32 BlockCount
|
|
)
|
|
{
|
|
|
|
if(Vcb->SparingTable) {
|
|
// use sparing table for relocation
|
|
uint32 i, BS, orig;
|
|
BS = Vcb->SparingBlockSize;
|
|
PSPARING_MAP Map;
|
|
|
|
Map = Vcb->SparingTable;
|
|
for(i=0;i<Vcb->SparingCount;i++,Map++) {
|
|
if( ((Lba >= (orig = Map->origLocation)) && (Lba < orig + BS)) ||
|
|
((Lba+BlockCount-1 >= orig) && (Lba+BlockCount-1 < orig + BS)) ||
|
|
((orig >= Lba) && (orig < Lba+BlockCount)) ||
|
|
((orig+BS >= Lba) && (orig+BS < Lba+BlockCount)) ) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
} else if(Vcb->Vat) {
|
|
// use VAT for relocation
|
|
uint32 i, root, j;
|
|
uint32* Map;
|
|
if(Lba < (root = Vcb->Partitions[Vcb->VatPartNdx].PartitionRoot))
|
|
return FALSE;
|
|
if(Lba+BlockCount >= Vcb->NWA)
|
|
return TRUE;
|
|
Map = &(Vcb->Vat[Lba-root/*+i*/]);
|
|
for(i=0; i<BlockCount; i++, Map++) {
|
|
if((j = (*Map)) &&
|
|
(j != Lba-root+i) &&
|
|
((j != UDF_VAT_FREE_ENTRY) || ((Lba+i) < Vcb->LastLBA)))
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
} // end UDFAreSectorsRelocated()
|
|
|
|
/*
|
|
This routine builds mapping for relocated extent
|
|
If relocation is not required (-1) will be returned
|
|
*/
|
|
PEXTENT_MAP
|
|
__fastcall
|
|
UDFRelocateSectors(
|
|
IN PVCB Vcb,
|
|
IN uint32 Lba,
|
|
IN uint32 BlockCount
|
|
)
|
|
{
|
|
if(!UDFAreSectorsRelocated(Vcb, Lba, BlockCount)) return UDF_NO_EXTENT_MAP;
|
|
|
|
PEXTENT_MAP Extent=NULL, Extent2;
|
|
uint32 NewLba, LastLba, j, i;
|
|
EXTENT_AD locExt;
|
|
|
|
LastLba = UDFRelocateSector(Vcb, Lba);
|
|
for(i=0, j=1; i<BlockCount; i++, j++) {
|
|
// create new entry if the extent in not contigous
|
|
if( ((NewLba = UDFRelocateSector(Vcb, Lba+i+1)) != (LastLba+1)) ||
|
|
(i==(BlockCount-1)) ) {
|
|
locExt.extLength = j << Vcb->BlockSizeBits;
|
|
locExt.extLocation = LastLba-j+1;
|
|
Extent2 = UDFExtentToMapping(&locExt);
|
|
if(!Extent) {
|
|
Extent = Extent2;
|
|
} else {
|
|
Extent = UDFMergeMappings(Extent, Extent2);
|
|
MyFreePool__(Extent2);
|
|
}
|
|
if(!Extent) return NULL;
|
|
j = 0;
|
|
}
|
|
LastLba = NewLba;
|
|
}
|
|
return Extent;
|
|
} // end UDFRelocateSectors()
|
|
|