////////////////////////////////////////////////////////////////////
// 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:

   mount.cpp

        Abstract:

   This file contains filesystem-specific routines
   responsible for Mount/Umount

*/

#include "udf.h"

/* FIXME */
#ifdef XCHG_DD
#undef XCHG_DD
#endif

#define XCHG_DD(a,b)              \
{                                 \
    ULONG  _temp_;                \
    PULONG _from_, _to_;          \
    _from_ = ((PULONG)&(b));      \
    _to_ =   ((PULONG)&(a));      \
    _temp_ = *_from_;             \
    *_from_ = *_to_;              \
    *_to_ = _temp_;               \
}

#define         UDF_BUG_CHECK_ID                UDF_FILE_UDF_INFO_MOUNT

OSSTATUS
__fastcall
UDFSetDstring(
    IN PUNICODE_STRING UName,
    IN dstring* Dest,
    IN uint32 Length
    );

#ifndef UDF_READ_ONLY_BUILD
/*
    This routine loads specified bitmap.
    It is also allocate space if the bitmap is not allocated.
 */
OSSTATUS
UDFPrepareXSpaceBitmap(
    IN PVCB Vcb,
 IN OUT PSHORT_AD XSpaceBitmap,
 IN OUT PEXTENT_INFO XSBMExtInfo,
 IN OUT int8** XSBM,
 IN OUT uint32* XSl
    )
{
    uint32 BS, j, LBS;
    uint32 plen;
    OSSTATUS status;
    EXTENT_MAP TmpExt;
    lb_addr locAddr;
    int8* _XSBM;
    uint16 Ident;
    SIZE_T ReadBytes;
    uint32 PartNum;

    if(!(XSpaceBitmap->extLength)) {
        *XSl = 0;
        *XSBM = NULL;
        return STATUS_SUCCESS;
    }

    PartNum = UDFGetPartNumByPartNdx(Vcb, Vcb->PartitionMaps-1);
    locAddr.partitionReferenceNum = (uint16)PartNum;
    plen = UDFPartStart(Vcb, PartNum) + UDFPartLen(Vcb, PartNum);

    BS = Vcb->BlockSize;
    LBS = Vcb->LBlockSize;

    *XSl = sizeof(SPACE_BITMAP_DESC) + ((plen+7)>>3);
    _XSBM = (int8*)DbgAllocatePool(NonPagedPool, (*XSl + BS - 1) & ~(BS-1) );
    *XSBM = _XSBM;

    switch (XSpaceBitmap->extLength >> 30) {
    case EXTENT_RECORDED_ALLOCATED: {
        locAddr.logicalBlockNum = XSpaceBitmap->extPosition;
        *XSl = min(XSpaceBitmap->extLength, *XSl);
        TmpExt.extLength = XSpaceBitmap->extLength = *XSl;
        TmpExt.extLocation = UDFPartLbaToPhys(Vcb, &locAddr);
        if(TmpExt.extLocation == LBA_OUT_OF_EXTENT) {
            BrutePoint();
        }
        XSBMExtInfo->Mapping = UDFExtentToMapping(&TmpExt);
        XSBMExtInfo->Offset = 0;
        XSBMExtInfo->Length = *XSl;
        break;
    }
    case EXTENT_NEXT_EXTENT_ALLOCDESC:
    case EXTENT_NOT_RECORDED_NOT_ALLOCATED: {
        // allocate space for bitmap
        if(!OS_SUCCESS(status = UDFAllocFreeExtent(Vcb, *XSl,
               UDFPartStart(Vcb, PartNum), UDFPartEnd(Vcb, PartNum), XSBMExtInfo, EXTENT_FLAG_ALLOC_SEQUENTIAL) ))
            return status;
        if(XSBMExtInfo->Mapping[1].extLength) {
            UDFPrint(("Can't allocate space for Freed Space bitmap\n"));
            *XSl = 0;
        } else {
            *XSl = (uint32)(XSBMExtInfo->Length);
            XSpaceBitmap->extPosition = UDFPhysLbaToPart(Vcb, PartNum, XSBMExtInfo->Mapping[0].extLocation);
        }
        break;
    }
    case EXTENT_NOT_RECORDED_ALLOCATED: {
        // record Alloc-Not-Rec
        locAddr.logicalBlockNum = XSpaceBitmap->extPosition;
        *XSl = min((XSpaceBitmap->extLength & UDF_EXTENT_LENGTH_MASK), *XSl);
        TmpExt.extLength = XSpaceBitmap->extLength = *XSl;
        TmpExt.extLocation = UDFPartLbaToPhys(Vcb, &locAddr);
        if(TmpExt.extLocation == LBA_OUT_OF_EXTENT) {
            BrutePoint();
        }
        XSBMExtInfo->Mapping = UDFExtentToMapping(&TmpExt);
        XSBMExtInfo->Offset = 0;
        XSBMExtInfo->Length = *XSl;
        break;
    }
    }

    if(!_XSBM) {
        BrutePoint();
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    switch (XSpaceBitmap->extLength >> 30) {
    case EXTENT_RECORDED_ALLOCATED: {
        // read descriptor & bitmap
        if((!OS_SUCCESS(status = UDFReadTagged(Vcb, *XSBM, (j = TmpExt.extLocation),
                             locAddr.logicalBlockNum, &Ident))) ||
           (Ident != TID_SPACE_BITMAP_DESC) ||
           (!OS_SUCCESS(status = UDFReadExtent(Vcb, XSBMExtInfo, 0, *XSl, FALSE, *XSBM, &ReadBytes))) ) {
            if(OS_SUCCESS(status)) {
                BrutePoint();
                status = STATUS_FILE_CORRUPT_ERROR;
            }
            if(XSBMExtInfo->Mapping) {
                MyFreePool__(XSBMExtInfo->Mapping);
                XSBMExtInfo->Mapping = NULL;
            }
            DbgFreePool(*XSBM);
            *XSl = 0;
            *XSBM = NULL;
            return status;
        } else {
//            BrutePoint();
        }
        return STATUS_SUCCESS;
    }
#if 0
    case EXTENT_NEXT_EXTENT_ALLOCDESC:
    case EXTENT_NOT_RECORDED_NOT_ALLOCATED:
    case EXTENT_NOT_RECORDED_ALLOCATED: {
        break;
    }
#endif
    }

    PSPACE_BITMAP_DESC XSDesc = (PSPACE_BITMAP_DESC)(*XSBM);

    XSpaceBitmap->extLength = (*XSl + LBS -1) & ~(LBS-1);
    RtlZeroMemory(*XSBM, *XSl);
    XSDesc->descTag.tagIdent = TID_SPACE_BITMAP_DESC;
    UDFSetUpTag(Vcb, &(XSDesc->descTag), 0, XSpaceBitmap->extPosition);
    XSDesc->numOfBits = plen;
    XSDesc->numOfBytes = (*XSl)-sizeof(SPACE_BITMAP_DESC);

    return STATUS_SUCCESS;
} // end UDFPrepareXSpaceBitmap()

/*
    This routine updates Freed & Unallocated space bitmaps
 */
OSSTATUS
UDFUpdateXSpaceBitmaps(
    IN PVCB Vcb,
    IN uint32 PartNum,
    IN PPARTITION_HEADER_DESC phd // partition header pointing to Bitmaps
    )
{
    uint32 i,j,d;
    uint32 plen, pstart, pend;
    int8* bad_bm;
    int8* old_bm;
    int8* new_bm;
    int8* fpart_bm;
    int8* upart_bm;
    OSSTATUS status, status2;
    int8* USBM=NULL;
    int8* FSBM=NULL;
    uint32 USl, FSl;
    EXTENT_INFO FSBMExtInfo, USBMExtInfo;
//    lb_addr locAddr;
    SIZE_T WrittenBytes;

    UDF_CHECK_BITMAP_RESOURCE(Vcb);

    plen = UDFPartLen(Vcb, PartNum);
//    locAddr.partitionReferenceNum = (uint16)PartNum;
    // prepare bitmaps for updating

    status =  UDFPrepareXSpaceBitmap(Vcb, &(phd->unallocatedSpaceBitmap), &USBMExtInfo, &USBM, &USl);
    status2 = UDFPrepareXSpaceBitmap(Vcb, &(phd->freedSpaceBitmap), &FSBMExtInfo, &FSBM, &FSl);
    if(!OS_SUCCESS(status) ||
       !OS_SUCCESS(status2)) {
        BrutePoint();
    }

    pstart = UDFPartStart(Vcb, PartNum);
    new_bm = Vcb->FSBM_Bitmap;
    old_bm = Vcb->FSBM_OldBitmap;
    bad_bm = Vcb->BSBM_Bitmap;

    if((status  == STATUS_INSUFFICIENT_RESOURCES) ||
       (status2 == STATUS_INSUFFICIENT_RESOURCES)) {
        // try to recover insufficient resources
        if(USl && USBMExtInfo.Mapping) {
            USl -= sizeof(SPACE_BITMAP_DESC);
            status  = UDFWriteExtent(Vcb, &USBMExtInfo, sizeof(SPACE_BITMAP_DESC), USl, FALSE, new_bm, &WrittenBytes);
#ifdef UDF_DBG
        } else {
            UDFPrint(("Can't update USBM\n"));
#endif // UDF_DBG
        }
        if(USBMExtInfo.Mapping) MyFreePool__(USBMExtInfo.Mapping);

        if(FSl && FSBMExtInfo.Mapping) {
            FSl -= sizeof(SPACE_BITMAP_DESC);
            status2 = UDFWriteExtent(Vcb, &FSBMExtInfo, sizeof(SPACE_BITMAP_DESC), FSl, FALSE, new_bm, &WrittenBytes);
        } else {
            status2 = status;
            UDFPrint(("Can't update FSBM\n"));
        }
        if(FSBMExtInfo.Mapping) MyFreePool__(FSBMExtInfo.Mapping);
    } else {
        // normal way to record BitMaps
        if(USBM) upart_bm =  USBM + sizeof(SPACE_BITMAP_DESC);
        if(FSBM) fpart_bm =  FSBM + sizeof(SPACE_BITMAP_DESC);
        pend = min(pstart + plen, Vcb->FSBM_BitCount);

        d=1<<Vcb->LB2B_Bits;
        // if we have some bad bits, mark corresponding area as BAD
        if(bad_bm) {
            for(i=pstart; i<pend; i++) {
                if(UDFGetBadBit(bad_bm, i)) {
                    // TODO: would be nice to add these blocks to unallocatable space
                    UDFSetUsedBits(new_bm, i & ~(d-1), d);
                }
            }
        }
        j=0;
        for(i=pstart; i<pend; i+=d) {
            if(UDFGetUsedBit(old_bm, i) && UDFGetFreeBit(new_bm, i)) {
                // sector was deallocated during last session
                if(USBM) UDFSetFreeBit(upart_bm, j);
                if(FSBM) UDFSetFreeBit(fpart_bm, j);
            } else if(UDFGetUsedBit(new_bm, i)) {
                // allocated
                if(USBM) UDFSetUsedBit(upart_bm, j);
                if(FSBM) UDFSetUsedBit(fpart_bm, j);
            }
            j++;
        }
        // flush updates
        if(USBM) {
            status  = UDFWriteExtent(Vcb, &USBMExtInfo, 0, USl, FALSE, USBM, &WrittenBytes);
            DbgFreePool(USBM);
            MyFreePool__(USBMExtInfo.Mapping);
        }
        if(FSBM) {
            status2 = UDFWriteExtent(Vcb, &FSBMExtInfo, 0, FSl, FALSE, FSBM, &WrittenBytes);
            DbgFreePool(FSBM);
            MyFreePool__(FSBMExtInfo.Mapping);
        } else {
            status2 = status;
        }
    }

    if(!OS_SUCCESS(status))
        return status;
    return status2;
} // end UDFUpdateXSpaceBitmaps()

/*
    This routine updates Partition Desc & associated data structures
 */
OSSTATUS
UDFUpdatePartDesc(
    PVCB Vcb,
    int8* Buf
    )
{
    PartitionDesc *p = (PartitionDesc *)Buf;
    uint32 i; // PartNdx
    tag* PTag;
    SIZE_T WrittenBytes;

    for(i=0; i<Vcb->PartitionMaps; i++)
    {
        if((UDFGetPartNumByPartNdx(Vcb,i) == p->partitionNumber) &&
           (!strcmp((int8*)&(p->partitionContents.ident), PARTITION_CONTENTS_NSR02) ||
            !strcmp((int8*)&(p->partitionContents.ident), PARTITION_CONTENTS_NSR03)))
        {
            PPARTITION_HEADER_DESC phd;

            phd = (PPARTITION_HEADER_DESC)(p->partitionContentsUse);
#ifdef UDF_DBG
            if(phd->unallocatedSpaceTable.extLength) {
                // rebuild unallocatedSpaceTable
                UDFPrint(("unallocatedSpaceTable (part %d)\n", i));
            }
            if(phd->freedSpaceTable.extLength) {
                // rebuild freedSpaceTable
                UDFPrint(("freedSpaceTable (part %d)\n", i));
            }
#endif // UDF_DBG
            UDFUpdateXSpaceBitmaps(Vcb, p->partitionNumber, phd);
            PTag = (tag*)Buf;
            UDFSetUpTag(Vcb, PTag, PTag->descCRCLength, PTag->tagLocation);
            UDFWriteSectors(Vcb, TRUE, PTag->tagLocation, 1, FALSE, Buf, &WrittenBytes);
        }
    }
    return STATUS_SUCCESS;
} // end UDFUpdatePartDesc()

/*
    This routine blanks Unalloc Space Desc
 *//*
OSSTATUS
UDFUpdateUSpaceDesc(
    IN PVCB Vcb,
    int8* Buf
    )
{
    PUNALLOC_SPACE_DESC usd;
    SIZE_T WrittenBytes;

    usd = (PUNALLOC_SPACE_DESC)Buf;
    usd->numAllocDescs = 0;
    RtlZeroMemory(Buf+sizeof(UNALLOC_SPACE_DESC), Vcb->BlockSize - sizeof(UNALLOC_SPACE_DESC));
    UDFSetUpTag(Vcb, &(usd->descTag), 0, usd->descTag.tagLocation);
    UDFWriteSectors(Vcb, TRUE, usd->descTag.tagLocation, 1, FALSE, Buf, &WrittenBytes);
    return STATUS_SUCCESS;
}*/

/*
   update Logical volume integrity descriptor
 */
OSSTATUS
UDFUpdateLogicalVolInt(
    PVCB            Vcb,
    BOOLEAN         Close
    )
{
    OSSTATUS    RC = STATUS_SUCCESS;
    uint32      i, len;
    SIZE_T      WrittenBytes;
//    uint32      lvid_count = 0;
    uint32      pSize;
    tag*        PTag;
    LogicalVolIntegrityDesc *lvid;
    LogicalVolIntegrityDescImpUse* LVID_iUse;
    LogicalVolHeaderDesc* LVID_hd;
    uint32*     partFreeSpace;
    BOOLEAN     equal = FALSE;

    if(Vcb->CDR_Mode)
        return STATUS_SUCCESS;
    if(!Vcb->LVid) {
        return STATUS_UNSUCCESSFUL;
    }

    UDFPrint(("UDF: Updating LVID @%x (%x)\n", Vcb->LVid_loc.extLocation, Vcb->LVid_loc.extLength));
    len = max(Vcb->LVid_loc.extLength, Vcb->BlockSize);
    lvid = Vcb->LVid;
    if(lvid->descTag.tagSerialNum > UDF_LVID_TTL) {
        // TODO: allocate space for new LVID
    }

    LVID_iUse = UDFGetLVIDiUse(Vcb);

    if((LVID_iUse->minUDFReadRev  == Vcb->minUDFReadRev) &&
       (LVID_iUse->minUDFWriteRev == Vcb->minUDFWriteRev) &&
       (LVID_iUse->maxUDFWriteRev == Vcb->maxUDFWriteRev) &&
       (LVID_iUse->numFiles == Vcb->numFiles) &&
       (LVID_iUse->numDirs  == Vcb->numDirs))
        equal = TRUE;

    LVID_iUse->minUDFReadRev  = Vcb->minUDFReadRev;
    LVID_iUse->minUDFWriteRev = Vcb->minUDFWriteRev;
    LVID_iUse->maxUDFWriteRev = Vcb->maxUDFWriteRev;

    LVID_iUse->numFiles = Vcb->numFiles;
    LVID_iUse->numDirs  = Vcb->numDirs;

#if 0
    UDFSetEntityID_imp(&(LVID_iUse->impIdent), UDF_ID_DEVELOPER);
#endif

    if(Close){
        UDFPrint(("UDF: Opening LVID\n"));
        lvid->integrityType = INTEGRITY_TYPE_CLOSE;
    } else {
        UDFPrint(("UDF: Closing LVID\n"));
        lvid->integrityType = INTEGRITY_TYPE_OPEN;
    }

    equal = equal && (Vcb->IntegrityType == lvid->integrityType);

    // update Free Space Table
    partFreeSpace = (uint32*)(lvid+1);
    for(i=0; i<lvid->numOfPartitions; i++) {
        pSize = UDFGetPartFreeSpace(Vcb, i) >> Vcb->LB2B_Bits;
        equal = equal && (partFreeSpace[i] == pSize);
        partFreeSpace[i] = pSize;
    }

    // Update LVID Header Descriptor
    LVID_hd = (LogicalVolHeaderDesc*)&(lvid->logicalVolContentsUse);
    equal = equal && (LVID_hd->uniqueID == Vcb->NextUniqueId);
    LVID_hd->uniqueID = Vcb->NextUniqueId;

    if(equal) {
        UDFPrint(("UDF: equal Ids\n"));
        return STATUS_SUCCESS;
    }

    PTag = &(lvid->descTag);
    lvid->lengthOfImpUse =
        sizeof(LogicalVolIntegrityDescImpUse);
    UDFSetUpTag(Vcb, PTag,
        sizeof(LogicalVolIntegrityDesc) +
        sizeof(uint32)*2*lvid->numOfPartitions +
        sizeof(LogicalVolIntegrityDescImpUse),
        PTag->tagLocation);

    Vcb->IntegrityType = INTEGRITY_TYPE_OPEN; // make happy auto-dirty
    RC = UDFWriteSectors(Vcb, TRUE, PTag->tagLocation, len >> Vcb->BlockSizeBits, FALSE, (int8*)(lvid), &WrittenBytes);
    WCacheFlushBlocks__(&(Vcb->FastCache), Vcb, PTag->tagLocation, len >> Vcb->BlockSizeBits);
    // update it here to prevent recursion
    Vcb->IntegrityType = lvid->integrityType;

    return RC;
} // end UDFUpdateLogicalVolInt()

/*
    This routine reads all sparing tables & stores them in contiguos memory
    space
 */
OSSTATUS
UDFUpdateSparingTable(
    IN PVCB Vcb
    )
{
    PSPARING_MAP RelocMap;
//    PSPARING_MAP NewRelocMap;
    OSSTATUS status = STATUS_SUCCESS;
    OSSTATUS status2 = STATUS_SUCCESS;
    uint32 i=0, BC, BC2;
    PSPARING_TABLE SparTable;
    SIZE_T ReadBytes;
//    uint32 n,m;
//    BOOLEAN merged;
    BOOLEAN sorted;

    UDFPrint(("UDF: Updating Sparable Part Map:\n"));
    if(!Vcb->SparingTableModified) return STATUS_SUCCESS;
    if(!Vcb->SparingTable) return STATUS_SUCCESS;

    BC = (Vcb->SparingTableLength >> Vcb->BlockSizeBits) + 1;
    SparTable = (PSPARING_TABLE)MyAllocatePool__(NonPagedPool, BC*Vcb->BlockSize);
    if(!SparTable) return STATUS_INSUFFICIENT_RESOURCES;
    // if a part of Sparing Table is already loaded,
    // update it with data from another one
    RelocMap = Vcb->SparingTable;
    // sort sparing table
    //merged = FALSE;
    do {
        sorted = FALSE;
        for(i=1;i<Vcb->SparingCount;i++) {
            if(RelocMap[i-1].origLocation > RelocMap[i].origLocation) {
                XCHG_DD(RelocMap[i-1].origLocation,   RelocMap[i].origLocation);
swp_loc:
                XCHG_DD(RelocMap[i-1].mappedLocation, RelocMap[i].mappedLocation);
                //merged = TRUE;
                sorted = TRUE;
            } else
            if(RelocMap[i-1].origLocation == SPARING_LOC_AVAILABLE &&
               RelocMap[i].origLocation   == SPARING_LOC_AVAILABLE &&
               RelocMap[i-1].mappedLocation > RelocMap[i].mappedLocation) {
                goto swp_loc;
            }
        }
    } while(sorted);

    for(i=0;i<Vcb->SparingCount;i++) {
        UDFPrint(("  @%x -> %x \n",
            RelocMap[i].origLocation, RelocMap[i].mappedLocation));
    }

    Vcb->SparingTableModified = FALSE;
//    if(!merged) {
//        UDFPrint(("  sparing table unchanged\n"));
//        MyFreePool__(SparTable);
//        return STATUS_SUCCESS;
//    }

    // walk through all available Sparing Tables
    for(i=0;i<Vcb->SparingTableCount;i++) {
        // read (next) table
        UDFPrint(("  sparing table @%x\n", Vcb->SparingTableLoc[i]));
        status = UDFReadSectors(Vcb, FALSE, Vcb->SparingTableLoc[i], 1, FALSE, (int8*)SparTable, &ReadBytes);
        // tag should be set to TID_UNUSED_DESC
        if(OS_SUCCESS(status) && (SparTable->descTag.tagIdent == TID_UNUSED_DESC)) {

            BC2 = ((sizeof(SPARING_TABLE) +
                    SparTable->reallocationTableLen*sizeof(SparingEntry) +
                    Vcb->BlockSize-1)
                                      >> Vcb->BlockSizeBits);
            if(BC2 > BC) {
                UDFPrint((" sizeSparingTable @%x too long: %x > %x\n",
                    Vcb->SparingTableLoc[i], BC2, BC
                    ));
                continue;
            }
            status = UDFReadSectors(Vcb, FALSE, Vcb->SparingTableLoc[i],
                BC2, FALSE, (int8*)SparTable, &ReadBytes);

            if(!OS_SUCCESS(status)) {
                UDFPrint((" Error reading sizeSparingTable @%x (%x)\n",
                    Vcb->SparingTableLoc[i], BC2
                    ));
                continue;
            }

            BC2 = ((sizeof(SPARING_TABLE) +
                    Vcb->SparingCount*sizeof(SparingEntry) +
                    Vcb->BlockSize-1)
                                      >> Vcb->BlockSizeBits);
            if(BC2 > BC) {
                UDFPrint((" new sizeSparingTable @%x too long: %x > %x\n",
                    Vcb->SparingTableLoc[i], BC2, BC
                    ));
                continue;
            }

            SparTable->reallocationTableLen = (USHORT)Vcb->SparingCount;
            RtlCopyMemory((SparTable+1), RelocMap, Vcb->SparingCount*sizeof(SparingEntry));
/*
            merged = FALSE;
            NewRelocMap = (PSPARING_MAP)(SparTable+1);
            for(n=0; n<SparTable->reallocationTableLen; n++) {
                for(m=0; m<Vcb->SparingCount; m++) {
                    if(RelocMap[m].mappedLocation == NewRelocMap[n].mappedLocation) {
                        if(RelocMap[m].origLocation != NewRelocMap[n].origLocation) {
                            UDFPrint(("  update @%x (%x) -> @%x (%x)\n",
                                NewRelocMap[m].origLocation, NewRelocMap[m].mappedLocation,
                                RelocMap[m].origLocation, RelocMap[m].mappedLocation));
                            merged = TRUE;
                        }
                    }
                }
            }
*/
//            if(merged) {
            UDFPrint(("UDF: record updated\n"));
            status = UDFWriteSectors(Vcb, FALSE, Vcb->SparingTableLoc[i], BC2, FALSE, (int8*)SparTable, &ReadBytes);
            if(!OS_SUCCESS(status)) {
                if(!OS_SUCCESS(status2)) {
                    status2 = status;
                }
//                }
            }
        }
    }
    MyFreePool__(SparTable);
    if(!OS_SUCCESS(status2)) {
        status = status2;
    }
    return status;
} // end UDFUpdateSparingTable()

/*
    update Logical volume descriptor
 */
OSSTATUS
UDFUpdateLogicalVol(
    IN PVCB            Vcb,
    IN UDF_VDS_RECORD  Lba,
    IN PUNICODE_STRING VolIdent
    )
{
    LogicalVolDesc* lvd = NULL;
#define CUR_IDENT_SZ (sizeof(lvd->logicalVolIdent))
    dstring CS0[CUR_IDENT_SZ];
    uint16 ident;
    SIZE_T WrittenBytes;
    OSSTATUS status = STATUS_SUCCESS;
//    OSSTATUS status2 = STATUS_SUCCESS;

    status = UDFUpdateSparingTable(Vcb);

    if(!(Vcb->CompatFlags & UDF_VCB_IC_W2K_COMPAT_VLABEL)) {
        goto Err_SetVI;
    }

    lvd = (LogicalVolDesc*)MyAllocatePool__(NonPagedPool, max(Vcb->BlockSize, sizeof(LogicalVolDesc)) );

    if(!lvd) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto Err_SetVI;
    }

    UDFPrint(("UDF: Updating LVD @%x (%x)\n", Lba.block, Vcb->BlockSize));

    status = UDFSetDstring(&(Vcb->VolIdent), (dstring*)&CS0, CUR_IDENT_SZ);
    if(!OS_SUCCESS(status)) {
        if(status == STATUS_INVALID_PARAMETER) {
            status = STATUS_INVALID_VOLUME_LABEL;
        }
        goto Err_SetVI;
    }

    if(!Lba.block) {
        status = STATUS_INVALID_PARAMETER;
        goto Err_SetVI;
    }
    status = UDFReadTagged(Vcb, (int8*)lvd, Lba.block, Lba.block, &ident);
    if(!OS_SUCCESS(status)) goto Err_SetVI;
    if(ident != TID_LOGICAL_VOL_DESC) {
        status = STATUS_FILE_CORRUPT_ERROR;
        goto Err_SetVI;
    }

    if(RtlCompareMemory(lvd->logicalVolIdent, CS0, CUR_IDENT_SZ) == CUR_IDENT_SZ) {
        // no changes
        UDFPrint(("UDF: equal VolIds\n"));
        status = STATUS_SUCCESS;
        goto Err_SetVI;
    }
    RtlCopyMemory(lvd->logicalVolIdent, CS0, CUR_IDENT_SZ);

    lvd->descTag.tagSerialNum --;
    UDFSetUpTag(Vcb, (tag*)lvd, lvd->descTag.descCRCLength, Lba.block);

    status = UDFWriteSectors(Vcb, TRUE, Lba.block, 1, FALSE, (int8*)lvd, &WrittenBytes);

Err_SetVI:
    if(lvd)
        MyFreePool__(lvd);

#undef CUR_IDENT_SZ
//#endif //0

    return status;
} // end UDFUpdateLogicalVol()

/*
    This routine updates volume descriptor sequence
 */
OSSTATUS
UDFUpdateVDS(
    IN PVCB Vcb,
    IN uint32 block,
    IN uint32 lastblock,
    IN uint32 flags
    )
{
    OSSTATUS status;
    int8*    Buf = (int8*)DbgAllocatePool(NonPagedPool,Vcb->LBlockSize);
    UDF_VDS_RECORD vds[VDS_POS_LENGTH];
    uint32 i,j;
    uint16 ident;

    if (!Buf) return STATUS_INSUFFICIENT_RESOURCES;
    RtlZeroMemory(vds, sizeof(UDF_VDS_RECORD) * VDS_POS_LENGTH);
    if(!OS_SUCCESS(status = UDFReadVDS(Vcb, block, lastblock, (PUDF_VDS_RECORD)&vds, Buf))) {
        DbgFreePool(Buf);
        return status;
    }

/*
    // update USD (if any)
    for (i=0; i<VDS_POS_LENGTH; i++) {
        if (vds[i].block) {
            status = UDFReadTagged(Vcb, Buf, vds[i].block, vds[i].block, &ident);
            if(OS_SUCCESS(status) && (i == VDS_POS_PARTITION_DESC)) {
                // load partition descriptor(s)
                int8*  Buf2 = (int8*)DbgAllocatePool(NonPagedPool,Vcb->BlockSize);
                if (!Buf2) {
                    DbgFreePool(Buf);
                    return STATUS_INSUFFICIENT_RESOURCES;
                }
                for (j=vds[i].block+1; j<vds[VDS_POS_TERMINATING_DESC].block; j++) {
                    UDFReadTagged(Vcb,Buf2, j, j, &ident);
                    if (ident == TID_UNALLOC_SPACE_DESC)
                        // This implememtation doesn't support USD ;) recording
                        // So, we'll make'em blank, but record all bitmaps
                        UDFUpdateUSpaceDesc(Vcb,Buf2);
                }
                DbgFreePool(Buf2);
                break;
            }
        }
    }*/
    for (i=0; i<VDS_POS_LENGTH; i++) {
        if (vds[i].block) {
            status = UDFReadTagged(Vcb, Buf, vds[i].block, vds[i].block, &ident);
            if(!OS_SUCCESS(status))
                continue;
            // update XBMs
            if(i == VDS_POS_PARTITION_DESC) {
                if(!(flags & 1))
                    continue;
                // update partition descriptor(s)
                int8*  Buf2 = (int8*)DbgAllocatePool(NonPagedPool,Vcb->BlockSize);
                if (!Buf2) {
                    DbgFreePool(Buf);
                    return STATUS_INSUFFICIENT_RESOURCES;
                }
                UDFUpdatePartDesc(Vcb,Buf);
                for (j=vds[i].block+1; j<vds[VDS_POS_TERMINATING_DESC].block; j++) {
                    UDFReadTagged(Vcb,Buf2, j, j, &ident);
                    if (ident == TID_PARTITION_DESC)
                        UDFUpdatePartDesc(Vcb,Buf2);
                }
                DbgFreePool(Buf2);
//                continue;
            } else
            // update Vol Ident Desc
            if(i == VDS_POS_LOGICAL_VOL_DESC) {
                status = UDFUpdateLogicalVol(Vcb, vds[VDS_POS_LOGICAL_VOL_DESC], &(Vcb->VolIdent));
                if(!OS_SUCCESS(status))
                    continue;
            }
        }
    }

    DbgFreePool(Buf);
    return status;
} // end UDFUpdateVDS()
#endif //UDF_READ_ONLY_BUILD

OSSTATUS
__fastcall
UDFSetDstring(
    IN PUNICODE_STRING UName,
    IN dstring* Dest,
    IN uint32 Length
    )
{
    uint8* CS0;
    SIZE_T len = Length-1;

    UDFCompressUnicode(UName, &CS0, &len);
    if(!CS0)
        return STATUS_INSUFFICIENT_RESOURCES;
    if(len > Length-1) {
        MyFreePool__(CS0);
        return STATUS_INVALID_PARAMETER;
    }
    RtlCopyMemory(Dest, CS0, len);
    MyFreePool__(CS0);
    if(len < Length-1)
        RtlZeroMemory(Dest+len, Length-1-len);
    Dest[Length-1] = (uint8)len;
    return TRUE;
} // end UDFSetDstring()

void
__fastcall
UDFGetDstring(
    IN OUT PUNICODE_STRING UName,
    IN dstring* Dest,
    IN uint32 Length
    )
{
    uint32 len = Dest[Length-1];

    UDFDecompressUnicode(UName, Dest, len, NULL);
    return;
} // end UDFGetDstring()

#ifndef UDF_READ_ONLY_BUILD
/*
    This routine updates Volume Label & some other features stored in
    VolIdentDesc
 */
OSSTATUS
UDFUpdateVolIdent(
    IN PVCB Vcb,
    IN UDF_VDS_RECORD Lba,
    IN PUNICODE_STRING VolIdent
    )
{
#define CUR_IDENT_SZ (sizeof(pvoldesc->volIdent))
    PrimaryVolDesc* pvoldesc = (PrimaryVolDesc*)MyAllocatePool__(NonPagedPool, max(Vcb->BlockSize, sizeof(PrimaryVolDesc)) );
    OSSTATUS status;
    dstring CS0[CUR_IDENT_SZ];
    uint16 ident;
    SIZE_T WrittenBytes;

    if(!pvoldesc) return STATUS_INSUFFICIENT_RESOURCES;

    UDFPrint(("UDF: Updating PVD @%x (%x)\n", Lba.block, Vcb->BlockSize));

    status = UDFSetDstring(&(Vcb->VolIdent), (dstring*)&CS0, CUR_IDENT_SZ);
    if(!OS_SUCCESS(status)) {
        if(status == STATUS_INVALID_PARAMETER) {
            status = STATUS_INVALID_VOLUME_LABEL;
        }
        goto Err_SetVI;
    }

    if(!Lba.block) {
        status = STATUS_INVALID_PARAMETER;
        goto Err_SetVI;
    }
    status = UDFReadTagged(Vcb, (int8*)pvoldesc, Lba.block, Lba.block, &ident);
    if(!OS_SUCCESS(status)) goto Err_SetVI;
    if(ident != TID_PRIMARY_VOL_DESC) {
        status = STATUS_FILE_CORRUPT_ERROR;
        goto Err_SetVI;
    }

    if(RtlCompareMemory(pvoldesc->volIdent, CS0, CUR_IDENT_SZ) == CUR_IDENT_SZ) {
        // no changes
        status = STATUS_SUCCESS;
        goto Err_SetVI;
    }
    RtlCopyMemory(pvoldesc->volIdent, CS0, CUR_IDENT_SZ);

    pvoldesc->descTag.tagSerialNum --;
    UDFSetUpTag(Vcb, (tag*)pvoldesc, pvoldesc->descTag.descCRCLength, Lba.block);

    status = UDFWriteSectors(Vcb, TRUE, Lba.block, 1, FALSE, (int8*)pvoldesc, &WrittenBytes);
Err_SetVI:
    MyFreePool__(pvoldesc);
    return status;

#undef CUR_IDENT_SZ
} // end UDFUpdateVolIdent()
#endif //UDF_READ_ONLY_BUILD

OSSTATUS
UDFUpdateNonAllocated(
    IN PVCB Vcb
    )
{
    uint32 PartNum;
    uint32 i;
    uint32 plen, pstart, pend;
    int8* bad_bm;
    EXTENT_AD Ext;
    PEXTENT_MAP Map = NULL;
    PEXTENT_INFO DataLoc;

    UDFPrint(("UDFUpdateNonAllocated:\n"));
    if(!Vcb->NonAllocFileInfo) {
        return STATUS_SUCCESS;
    }
    if(!(bad_bm = Vcb->BSBM_Bitmap)) {
        return STATUS_SUCCESS;
    }

    DataLoc = &(Vcb->NonAllocFileInfo->Dloc->DataLoc);
    ASSERT(!DataLoc->Offset);
    if(Vcb->NonAllocFileInfo->Dloc->DataLoc.Offset) {
        UDFPrint(("NonAllocFileInfo in IN_ICB mode !!!\n"));
        return STATUS_SUCCESS;
    }
    PartNum = UDFGetPartNumByPhysLba(Vcb, Vcb->NonAllocFileInfo->Dloc->FELoc.Mapping[0].extLocation);
    pstart = UDFPartStart(Vcb, PartNum);
    plen = UDFPartLen(Vcb, PartNum);
    pend = min(pstart + plen, Vcb->FSBM_BitCount);

    //BrutePoint();
    for(i=pstart; i<pend; i++) {
        if(!UDFGetBadBit(bad_bm, i))
            continue;
        // add BAD blocks to unallocatable space
        // if the block is already in NonAllocatable, ignore it
        if(UDFLocateLbaInExtent(Vcb, DataLoc->Mapping, i) != LBA_OUT_OF_EXTENT) {
            UDFPrint(("lba %#x is already in NonAllocFileInfo\n", i));
            continue;
        }
        UDFPrint(("add lba %#x to NonAllocFileInfo\n", i));
        DataLoc->Modified = TRUE;
        Ext.extLength = Vcb->LBlockSize;
        // align lba on LogicalBlock boundary
        Ext.extLocation = i & ~((1<<Vcb->LB2B_Bits) - 1);
        Map = UDFExtentToMapping(&Ext);
        DataLoc->Mapping = UDFMergeMappings(DataLoc->Mapping, Map);
    }
    UDFPackMapping(Vcb, DataLoc);
    DataLoc->Length = UDFGetExtentLength(DataLoc->Mapping);
    UDFFlushFile__(Vcb, Vcb->NonAllocFileInfo);

    // ensure that BAD space is marked as USED
    UDFMarkSpaceAsXXX(Vcb, 0, &(DataLoc->Mapping[0]), AS_USED); // mark as used

    UDFPrint(("UDFUpdateNonAllocated: done\n"));
    return STATUS_SUCCESS;
} // end UDFUpdateNonAllocated()

/*
    This routine rebuilds & flushes all system areas
 */
OSSTATUS
UDFUmount__(
    IN PVCB Vcb
    )
{
#ifndef UDF_READ_ONLY_BUILD
    uint32 flags = 0;

    if((Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY)
        || !Vcb->Modified)
        return STATUS_SUCCESS;
    // prevent discarding metadata
    Vcb->VCBFlags |= UDF_VCB_ASSUME_ALL_USED;
    if(Vcb->CDR_Mode) {
        // flush internal cache
        if(WCacheGetWriteBlockCount__(&(Vcb->FastCache)) >= (Vcb->WriteBlockSize >> Vcb->BlockSizeBits) )
            WCacheFlushAll__(&(Vcb->FastCache), Vcb);
        // record VAT
        return UDFRecordVAT(Vcb);
    }

    UDFFlushAllCachedAllocations(Vcb, UDF_PREALLOC_CLASS_FE);
    UDFFlushAllCachedAllocations(Vcb, UDF_PREALLOC_CLASS_DIR);

    if(Vcb->VerifyOnWrite) {
        UDFPrint(("UDF: Flushing cache for verify\n"));
        //WCacheFlushAll__(&(Vcb->FastCache), Vcb);
        WCacheFlushBlocks__(&(Vcb->FastCache), Vcb, 0, Vcb->LastLBA);
        UDFVFlush(Vcb);
    }

    // synchronize BAD Block bitmap and NonAllocatable
    UDFUpdateNonAllocated(Vcb);

    UDFAcquireResourceExclusive(&(Vcb->BitMapResource1),TRUE);

    // RAM mode
#ifdef UDF_DBG
    if(!OS_SUCCESS(UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr, &(Vcb->VolIdent))))
        UDFPrint(("Error updating VolIdent (1)\n"));
    if(!OS_SUCCESS(UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr2, &(Vcb->VolIdent))))
        UDFPrint(("Error updating VolIdent (2)\n"));
#else
    UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr, &(Vcb->VolIdent));
    UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr2, &(Vcb->VolIdent));
#endif // UDF_DBG

    UDF_CHECK_BITMAP_RESOURCE(Vcb);
    // check if we should update BM
    if(Vcb->FSBM_ByteCount == RtlCompareMemory(Vcb->FSBM_Bitmap, Vcb->FSBM_OldBitmap, Vcb->FSBM_ByteCount)) {
        flags &= ~1;
    } else {
        flags |= 1;
    }

#ifdef UDF_DBG
    if(!OS_SUCCESS(UDFUpdateVDS(Vcb, Vcb->VDS1, Vcb->VDS1 + Vcb->VDS1_Len, flags)))
        UDFPrint(("Error updating Main VDS\n"));
    if(!OS_SUCCESS(UDFUpdateVDS(Vcb, Vcb->VDS2, Vcb->VDS2 + Vcb->VDS2_Len, flags)))
        UDFPrint(("Error updating Reserve VDS\n"));
#else
    UDFUpdateVDS(Vcb, Vcb->VDS1, Vcb->VDS1 + Vcb->VDS1_Len, flags);
    UDFUpdateVDS(Vcb, Vcb->VDS2, Vcb->VDS2 + Vcb->VDS2_Len, flags);
#endif // UDF_DBG

    // Update Integrity Desc if any
    if(Vcb->LVid && Vcb->origIntegrityType == INTEGRITY_TYPE_CLOSE) {
        UDFUpdateLogicalVolInt(Vcb, TRUE);
    }

    if(flags & 1)
        RtlCopyMemory(Vcb->FSBM_OldBitmap, Vcb->FSBM_Bitmap, Vcb->FSBM_ByteCount);

//skip_update_bitmap:

    Vcb->VCBFlags &= ~UDF_VCB_ASSUME_ALL_USED;

    UDFReleaseResource(&(Vcb->BitMapResource1));
#endif //UDF_READ_ONLY_BUILD

    return STATUS_SUCCESS;
} // end UDFUmount__()

/*************************************************************************/

/*
    Find an anchor volume descriptor.
    The UDFGetDiskInfoAndVerify() will invoke this routine to find & check
    Anchor Volume Descriptors on the target device
*/
lba_t
UDFFindAnchor(
    PVCB           Vcb           // Volume control block
    )
{
//    OSSTATUS    RC = STATUS_SUCCESS;

    uint16 ident;
    uint32 i;
    uint32 LastBlock;
    OSSTATUS status;
    int8* Buf = (int8*)MyAllocatePool__(NonPagedPool,Vcb->BlockSize);
    BOOLEAN MRW_candidate;
    BOOLEAN IsMRW = (Vcb->MRWStatus != 0);
    if(!Buf)
        return 0;

    UDFPrint(("UDFFindAnchor\n"));
    // init probable locations...
    RtlZeroMemory(&(Vcb->Anchor), sizeof(Vcb->Anchor));
    Vcb->Anchor[0] = 256 + Vcb->FirstLBALastSes;
    Vcb->Anchor[1] = 512 + Vcb->FirstLBALastSes;
    Vcb->Anchor[2] = 256 + Vcb->TrackMap[Vcb->LastTrackNum].FirstLba;
    Vcb->Anchor[3] = 512 + Vcb->TrackMap[Vcb->LastTrackNum].FirstLba;
    Vcb->Anchor[4] = Vcb->LastLBA - 256;
    Vcb->Anchor[5] = Vcb->LastLBA - 256 + 1;
    Vcb->Anchor[6] = Vcb->LastLBA - 256 - 2;
    // vat locations
    Vcb->Anchor[7] = Vcb->LastLBA - 2;
    Vcb->Anchor[8] = Vcb->LastLBA;
    Vcb->Anchor[9] = Vcb->LastLBA - 512;
//    Vcb->Anchor[7] = Vcb->LastLBA - 256 - 7;
//    Vcb->Anchor[8] = Vcb->LastLBA - 512 - 2;
//    Vcb->Anchor[9] = Vcb->LastLBA - 512 - 7;

    LastBlock = 0;
    // ... and check them
    for (i=0; i<sizeof(Vcb->Anchor)/sizeof(int); i++) {
        if(Vcb->Anchor[i] > Vcb->LastLBA)
            Vcb->Anchor[i] = 0;
        MRW_candidate = FALSE;
        if(Vcb->Anchor[i]) {
            UDFPrint(("check Anchor %x\n", Vcb->Anchor[i]));
            if(!OS_SUCCESS(status = UDFReadTagged(Vcb,Buf,
                Vcb->Anchor[i], Vcb->Anchor[i], &ident))) {

                // Fucking MRW...
                if(!IsMRW && (i<2) &&
                   (Vcb->CompatFlags & UDF_VCB_IC_MRW_ADDR_PROBLEM)) {
                    if(OS_SUCCESS(status = UDFReadTagged(Vcb,Buf,
                        Vcb->Anchor[i]+MRW_DMA_OFFSET, Vcb->Anchor[i], &ident))) {
                        // do MRW workaround.....
                        UDFPrint(("UDF: looks like we have MRW....\n"));
                        MRW_candidate = TRUE;
                        goto MRW_workaround;
                    }
                }

                Vcb->Anchor[i] = 0;
                if(status == STATUS_NONEXISTENT_SECTOR) {
                    UDFPrint(("UDF: disk seems to be incomplete\n"));
                    break;
                }
            } else {
MRW_workaround:
                if((ident != TID_ANCHOR_VOL_DESC_PTR) && ((i<6) ||
                    (ident != TID_FILE_ENTRY && ident != TID_EXTENDED_FILE_ENTRY))) {
                    Vcb->Anchor[i] = 0;
                } else {
                    UDFPrint(("UDF: Found AVD at %x (point %d)\n",Vcb->Anchor[i], i));
                    if(!LastBlock)
                        LastBlock = Vcb->LastLBA;
                    if(MRW_candidate) {
                        UDFPrint(("UDF: looks like we _*really*_ have MRW....\n"));
                        IsMRW = TRUE;
                        ASSERT(Vcb->LastReadTrack == 1);
                        Vcb->TrackMap[Vcb->LastReadTrack].Flags |= TrackMap_FixMRWAddressing;
                        WCachePurgeAll__(&(Vcb->FastCache), Vcb);
                        UDFPrint(("UDF: MRW on non-MRW drive => ReadOnly"));
                        Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;

                        UDFRegisterFsStructure(Vcb, Vcb->Anchor[i], Vcb->BlockSize);

                    }
                }
            }
        }
    }

    UDFPrint(("UDF: -----------------\nUDF: Last block %x\n",LastBlock));
    MyFreePool__(Buf);
    return LastBlock;
} // end UDFFindAnchor()

/*
    Look for Volume recognition sequence
 */
uint32
UDFFindVRS(
    PVCB           Vcb
    )
{
    VolStructDesc  *vsd = NULL;
    uint32       offset;
    uint32       retStat = 0;
    uint32       BeginOffset = Vcb->FirstLBA;
    OSSTATUS     RC;
    int8*        buffer = (int8*)MyAllocatePool__(NonPagedPool,Vcb->BlockSize);
    SIZE_T       ReadBytes;

    if(!buffer) return 0;
    // Relative to First LBA in Last Session
    offset = Vcb->FirstLBA + 0x10;

    UDFPrint(("UDFFindVRS:\n"));

    // Process the sequence (if applicable)
    for (;(offset-BeginOffset <=0x20); offset ++) {
        // Read a block
        RC = UDFReadSectors(Vcb, FALSE, offset, 1, FALSE, buffer, &ReadBytes);
        if(!OS_SUCCESS(RC)) continue;

        // Look for ISO descriptors
        vsd = (VolStructDesc *)(buffer);

        if(vsd->stdIdent[0]) {
            if(!strncmp((int8*)(&vsd->stdIdent), STD_ID_CD001, STD_ID_LEN))
            {
                retStat |= VRS_ISO9660_FOUND;
                switch (vsd->structType)
                {
                    case 0:
                        UDFPrint(("UDF: ISO9660 Boot Record found\n"));
                        break;
                    case 1:
                        UDFPrint(("UDF: ISO9660 Primary Volume Descriptor found\n"));
                        break;
                    case 2:
                        UDFPrint(("UDF: ISO9660 Supplementary Volume Descriptor found\n"));
                        break;
                    case 3:
                        UDFPrint(("UDF: ISO9660 Volume Partition Descriptor found\n"));
                        break;
                    case 255:
                        UDFPrint(("UDF: ISO9660 Volume Descriptor Set Terminator found\n"));
                        break;
                    default:
                        UDFPrint(("UDF: ISO9660 VRS (%u) found\n", vsd->structType));
                        break;
                }
            }
            else if(!strncmp((int8*)(&vsd->stdIdent), STD_ID_BEA01, STD_ID_LEN))
            {
                UDFPrint(("UDF: BEA01 Found\n"));
            }
            else if(!strncmp((int8*)(&vsd->stdIdent), STD_ID_TEA01, STD_ID_LEN))
            {
                UDFPrint(("UDF: TEA01 Found\n"));
                break;
            }
            else if(!strncmp((int8*)(&vsd->stdIdent), STD_ID_NSR02, STD_ID_LEN))
            {
                retStat |= VRS_NSR02_FOUND;
                UDFPrint(("UDF: NSR02 Found\n"));
                break;
            }
            else if(!strncmp((int8*)(&vsd->stdIdent), STD_ID_NSR03, STD_ID_LEN))
            {
                retStat |= VRS_NSR03_FOUND;
                UDFPrint(("UDF: NSR03 Found\n"));
                break;
            }
        }
    }

    MyFreePool__(buffer);

    return retStat;
} // end UDFFindVRS()

/*
    process Primary volume descriptor
 */
void
UDFLoadPVolDesc(
    PVCB Vcb,
    int8* Buf // pointer to buffer containing PVD
    )
{
    PrimaryVolDesc *pvoldesc;
//    OSSTATUS    RC = STATUS_SUCCESS;

    pvoldesc = (PrimaryVolDesc *)Buf;
    UDFPrint(("UDF: PrimaryVolDesc:\n"));
    UDFPrint(("volDescSeqNum     = %d\n", pvoldesc->volDescSeqNum));
    UDFPrint(("primaryVolDescNum = %d\n", pvoldesc->primaryVolDescNum));
    // remember recording time...
    Vcb->VolCreationTime = UDFTimeToNT(&(pvoldesc->recordingDateAndTime));
    // ...VolIdent...
#define CUR_IDENT_SZ (sizeof(pvoldesc->volIdent))
    if (Vcb->VolIdent.Buffer) {
        MyFreePool__(Vcb->VolIdent.Buffer);
    }
    UDFGetDstring(&(Vcb->VolIdent), (dstring*)&(pvoldesc->volIdent), CUR_IDENT_SZ);
#undef CUR_IDENT_SZ
    UDFPrint(("volIdent[] = '%ws'\n", Vcb->VolIdent.Buffer));
#ifdef UDF_DBG
    UDFPrint(("volSeqNum         = %d\n", pvoldesc->volSeqNum));
    UDFPrint(("maxVolSeqNum      = %d\n", pvoldesc->maxVolSeqNum));
    UDFPrint(("interchangeLvl    = %d\n", pvoldesc->interchangeLvl));
    UDFPrint(("maxInterchangeLvl = %d\n", pvoldesc->maxInterchangeLvl));
    UDFPrint(("charSetList       = %d\n", pvoldesc->charSetList));
    UDFPrint(("maxCharSetList    = %d\n", pvoldesc->maxCharSetList));
    // ...& just print VolSetIdent
    UNICODE_STRING      instr;
#define CUR_IDENT_SZ (sizeof(pvoldesc->volSetIdent))
    UDFGetDstring(&instr, (dstring*)&(pvoldesc->volSetIdent), CUR_IDENT_SZ);
#undef CUR_IDENT_SZ
    UDFPrint(("volSetIdent[] = '%ws'\n", instr.Buffer));
//    UDFPrint(("maxInterchangeLvl = %d\n", pvoldesc->maxInterchangeLvl));
    UDFPrint(("flags             = %x\n", pvoldesc->flags));
    if(instr.Buffer) MyFreePool__(instr.Buffer);
#endif // UDF_DBG
} // end UDFLoadPVolDesc()

/*
   load Logical volume integrity descriptor
 */
OSSTATUS
UDFLoadLogicalVolInt(
    PDEVICE_OBJECT  DeviceObject,
    PVCB            Vcb,
    extent_ad       loc
    )
{
    OSSTATUS    RC = STATUS_SUCCESS;
    uint32      len;
    SIZE_T      _ReadBytes;
    int8*       Buf = NULL;
    uint16      ident;
    LogicalVolIntegrityDescImpUse* LVID_iUse;
    LogicalVolHeaderDesc* LVID_hd;
    extent_ad   last_loc;
    BOOLEAN     read_last = FALSE;
    uint32      lvid_count = 0;

    ASSERT(!Vcb->LVid);
    if(Vcb->LVid) {
        MyFreePool__(Vcb->LVid);
        Vcb->LVid = NULL;
    }
    // walk through all sectors inside LogicalVolumeIntegrityDesc
    while(loc.extLength) {
        UDFPrint(("UDF: Reading LVID @%x (%x)\n", loc.extLocation, loc.extLength));
        len = max(loc.extLength, Vcb->BlockSize);
        Buf = (int8*)MyAllocatePool__(NonPagedPool,len);
        if(!Buf)
            return STATUS_INSUFFICIENT_RESOURCES;
        RC = UDFReadTagged(Vcb,Buf, loc.extLocation, loc.extLocation, &ident);
        if(!OS_SUCCESS(RC)) {
exit_with_err:
            UDFPrint(("UDF: Reading LVID @%x (%x) failed.\n", loc.extLocation, loc.extLength));
            switch(Vcb->PartitialDamagedVolumeAction) {
            case UDF_PART_DAMAGED_RO:
                UDFPrint(("UDF: Switch to r/o mode.\n"));
                Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
                Vcb->UserFSFlags |= UDF_USER_FS_FLAGS_MEDIA_DEFECT_RO;
                RC = STATUS_SUCCESS;
                break;
            case UDF_PART_DAMAGED_NO:
                UDFPrint(("UDF: Switch to raw mount mode, return UNRECOGNIZED_VOLUME.\n"));
                Vcb->VCBFlags |= UDF_VCB_FLAGS_RAW_DISK;
                //RC = STATUS_WRONG_VOLUME;
                break;
            case UDF_PART_DAMAGED_RW:
            default:
                UDFPrint(("UDF: Keep r/w mode for your own risk.\n"));
                RC = STATUS_SUCCESS;
                // asume we have INTEGRITY_TYPE_CLOSE
                Vcb->IntegrityType = INTEGRITY_TYPE_CLOSE;
                break;
            }

            MyFreePool__(Buf);
            return RC;
        }
        UDFRegisterFsStructure(Vcb, loc.extLocation, len);
        // handle Terminal Entry
        if(ident == TID_TERMINAL_ENTRY) {
            read_last = TRUE;
            MyFreePool__(Buf);
            Vcb->LVid = NULL;
            loc = last_loc;
            continue;
        } else
        if(ident != TID_LOGICAL_VOL_INTEGRITY_DESC) {
            RC = STATUS_DISK_CORRUPT_ERROR;
            goto exit_with_err;
        }

        Vcb->LVid = (LogicalVolIntegrityDesc *)Buf;
        RC = UDFReadData(Vcb, TRUE, ((uint64)(loc.extLocation)) << Vcb->BlockSizeBits, len, FALSE, Buf, &_ReadBytes);
        // update info
        if( !read_last &&
            Vcb->LVid->nextIntegrityExt.extLength) {
            // go to next LVID
            last_loc = loc;
            loc = Vcb->LVid->nextIntegrityExt;
            Vcb->LVid = NULL;
            lvid_count++;
            if(lvid_count > UDF_MAX_LVID_CHAIN_LENGTH) {
                RC = STATUS_DISK_CORRUPT_ERROR;
                goto exit_with_err;
            }
            MyFreePool__(Buf);
            continue;
        }
        // process last LVID
        Vcb->origIntegrityType =
            Vcb->IntegrityType = Vcb->LVid->integrityType;
        Vcb->LVid_loc = loc;

        LVID_iUse = UDFGetLVIDiUse(Vcb);

        UDFPrint(("UDF: Last LVID:\n"));
        UDFPrint(("     minR: %x\n",LVID_iUse->minUDFReadRev ));
        UDFPrint(("     minW: %x\n",LVID_iUse->minUDFWriteRev));
        UDFPrint(("     maxW: %x\n",LVID_iUse->maxUDFWriteRev));
        UDFPrint(("     Type: %s\n",!Vcb->IntegrityType ? "Open" : "Close"));

        Vcb->minUDFReadRev  = LVID_iUse->minUDFReadRev;
        Vcb->minUDFWriteRev = LVID_iUse->minUDFWriteRev;
        Vcb->maxUDFWriteRev = LVID_iUse->maxUDFWriteRev;

        Vcb->numFiles = LVID_iUse->numFiles;
        Vcb->numDirs  = LVID_iUse->numDirs;
        UDFPrint(("     nFiles: %x\n",Vcb->numFiles ));
        UDFPrint(("     nDirs: %x\n",Vcb->numDirs ));

        // Check if we can understand this format
        if(Vcb->minUDFReadRev > UDF_MAX_READ_REVISION)
            RC = STATUS_UNRECOGNIZED_VOLUME;
        // Check if we know how to write here
        if(Vcb->minUDFWriteRev > UDF_MAX_WRITE_REVISION) {
            UDFPrint(("     Target FS requires: %x Revision => ReadOnly\n",Vcb->minUDFWriteRev));
            Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
            Vcb->UserFSFlags |= UDF_USER_FS_FLAGS_NEW_FS_RO;
        }

        LVID_hd = (LogicalVolHeaderDesc*)&(Vcb->LVid->logicalVolContentsUse);
        Vcb->NextUniqueId = LVID_hd->uniqueID;
        UDFPrint(("     Next FID: %x\n",Vcb->NextUniqueId));

        break;
    }

    return RC;
} // end UDFLoadLogicalVolInt()


/*
    load Logical volume descriptor
 */
OSSTATUS
UDFLoadLogicalVol(
    PDEVICE_OBJECT  DeviceObject,
    PVCB            Vcb,
    int8*           Buf,
    lb_addr         *fileset
    )
{
    LogicalVolDesc *lvd = (LogicalVolDesc *)Buf;
    uint16 i, offset;
    uint8 type;
    OSSTATUS status = STATUS_SUCCESS;
    UDFPrint(("UDF: LogicalVolDesc\n"));
    // Validate partition map counter
    if(!(Vcb->Partitions)) {
        Vcb->PartitionMaps = lvd->numPartitionMaps;
        Vcb->Partitions = (PUDFPartMap)MyAllocatePool__(NonPagedPool, sizeof(UDFPartMap) * Vcb->PartitionMaps );
        if(!Vcb->Partitions)
            return STATUS_INSUFFICIENT_RESOURCES;
    } else {
        if(Vcb->PartitionMaps != lvd->numPartitionMaps)
            return STATUS_DISK_CORRUPT_ERROR;
    }
    UDFPrint(("UDF: volDescSeqNum = %x\n", lvd->volDescSeqNum));
    // Get logical block size (may be different from physical)
    Vcb->LBlockSize = lvd->logicalBlockSize;
    // Get current UDF revision
    // Get Read-Only flags
    UDFReadEntityID_Domain(Vcb, &(lvd->domainIdent));

    if(Vcb->LBlockSize < Vcb->BlockSize)
        return STATUS_DISK_CORRUPT_ERROR;
    switch(Vcb->LBlockSize) {
    case 512: Vcb->LBlockSizeBits = 9; break;
    case 1024: Vcb->LBlockSizeBits = 10; break;
    case 2048: Vcb->LBlockSizeBits = 11; break;
    case 4096: Vcb->LBlockSizeBits = 12; break;
    case 8192: Vcb->LBlockSizeBits = 13; break;
    case 16384: Vcb->LBlockSizeBits = 14; break;
    case 32768: Vcb->LBlockSizeBits = 15; break;
    case 65536: Vcb->LBlockSizeBits = 16; break;
    default:
        UDFPrint(("UDF: Bad block size (%ld)\n", Vcb->LBlockSize));
        return STATUS_DISK_CORRUPT_ERROR;
    }
    UDFPrint(("UDF: logical block size (%ld)\n", Vcb->LBlockSize));
    Vcb->LB2B_Bits = Vcb->LBlockSizeBits - Vcb->BlockSizeBits;
    UDFPrint(("UDF: mapTableLength = %x\n", lvd->mapTableLength));
    UDFPrint(("UDF: numPartitionMaps = %x\n", lvd->numPartitionMaps));
    // walk through all available part maps
    for (i=0,offset=0;
         i<Vcb->PartitionMaps && offset<lvd->mapTableLength;
         i++,offset+=((GenericPartitionMap *)( ((uint8*)(lvd+1))+offset) )->partitionMapLength)
    {
        GenericPartitionMap* gpm = (GenericPartitionMap *)(((uint8*)(lvd+1))+offset);
        type = gpm->partitionMapType;
        UDFPrint(("Partition (%d) type %x, len %x\n", i, type, gpm->partitionMapLength));
        if(type == PARTITION_MAP_TYPE_1)
        {
            GenericPartitionMap1 *gpm1 = (GenericPartitionMap1 *)(((uint8*)(lvd+1))+offset);

            Vcb->Partitions[i].PartitionType = UDF_TYPE1_MAP15;
            Vcb->Partitions[i].VolumeSeqNum = gpm1->volSeqNum;
            Vcb->Partitions[i].PartitionNum = gpm1->partitionNum;
            status = STATUS_SUCCESS;
        }
        else if(type == PARTITION_MAP_TYPE_2)
        {
            UdfPartitionMap2* upm2 = (UdfPartitionMap2 *)(((uint8*)(lvd+1))+offset);
            if(!strncmp((int8*)&(upm2->partIdent.ident), UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL)))
            {
                UDFIdentSuffix* udfis =
                    (UDFIdentSuffix*)&(upm2->partIdent.identSuffix);

                if( (udfis->currentRev == 0x0150)/* ||
                    (Vcb->CurrentUDFRev == 0x0150)*/ ) {
                    UDFPrint(("Found VAT 1.50\n"));
                    Vcb->Partitions[i].PartitionType = UDF_VIRTUAL_MAP15;
                } else
                if( (udfis->currentRev == 0x0200) ||
                    (udfis->currentRev == 0x0201) /*||
                    (Vcb->CurrentUDFRev == 0x0200) ||
                    (Vcb->CurrentUDFRev == 0x0201)*/ ) {
                    UDFPrint(("Found VAT 2.00\n"));
                    Vcb->Partitions[i].PartitionType = UDF_VIRTUAL_MAP20;
                }
                status = STATUS_SUCCESS;
            }
            else if(!strncmp((int8*)&(upm2->partIdent.ident), UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE)))
            {
                UDFPrint(("Load sparing table\n"));
                PSPARABLE_PARTITION_MAP spm = (PSPARABLE_PARTITION_MAP)(((uint8*)(lvd+1))+offset);
                Vcb->Partitions[i].PartitionType = UDF_SPARABLE_MAP15;
                status = UDFLoadSparingTable(Vcb, spm);
            }
            else if(!strncmp((int8*)&(upm2->partIdent.ident), UDF_ID_METADATA, strlen(UDF_ID_METADATA)))
            {
                UDFPrint(("Found metadata partition\n"));
//                PMETADATA_PARTITION_MAP mpm = (PMETADATA_PARTITION_MAP)(((uint8*)(lvd+1))+offset);
                Vcb->Partitions[i].PartitionType = UDF_METADATA_MAP25;
                //status = UDFLoadSparingTable(Vcb, spm);
            }
            else
            {
                UDFPrint(("Unknown ident: %s\n", upm2->partIdent.ident));
                continue;
            }
            Vcb->Partitions[i].VolumeSeqNum = upm2->volSeqNum;
            Vcb->Partitions[i].PartitionNum = upm2->partitionNum;
        }
    }

    if(fileset) {
        // remember FileSet location
        long_ad *la = (long_ad *)&(lvd->logicalVolContentsUse[0]);
        *fileset = (la->extLocation);
        UDFPrint(("FileSet found in LogicalVolDesc at block=%x, partition=%d\n",
            fileset->logicalBlockNum,
            fileset->partitionReferenceNum));
    }
    if(OS_SUCCESS(status)) {
        // load Integrity Desc if any
        if(lvd->integritySeqExt.extLength)
            status = UDFLoadLogicalVolInt(DeviceObject,Vcb,lvd->integritySeqExt);
    }
    return status;
} // end UDFLoadLogicalVol()

OSSTATUS
UDFLoadBogusLogicalVol(
    PDEVICE_OBJECT  DeviceObject,
    PVCB            Vcb,
    int8*           Buf,
    lb_addr         *fileset
    )
{
//    LogicalVolDesc *lvd = (LogicalVolDesc *)Buf;
    UDFPrint(("UDF: Bogus LogicalVolDesc\n"));
    // Validate partition map counter
    if(!(Vcb->Partitions)) {
        Vcb->PartitionMaps = 1;
        Vcb->Partitions = (PUDFPartMap)MyAllocatePool__(NonPagedPool, sizeof(UDFPartMap) * Vcb->PartitionMaps );
        if(!Vcb->Partitions)
            return STATUS_INSUFFICIENT_RESOURCES;
    } else {
        if(Vcb->PartitionMaps != 1)
            return STATUS_DISK_CORRUPT_ERROR;
    }
    UDFPrint(("UDF: volDescSeqNum = %x\n", 0));
    // Get logical block size (may be different from physical)
    Vcb->LBlockSize = 2048;
    // Get current UDF revision
    // Get Read-Only flags
//    UDFReadEntityID_Domain(Vcb, &(lvd->domainIdent));

    if(Vcb->LBlockSize < Vcb->BlockSize)
        return STATUS_DISK_CORRUPT_ERROR;
    Vcb->LBlockSizeBits = 11;
    UDFPrint(("UDF: logical block size (%ld)\n", Vcb->LBlockSize));
    Vcb->LB2B_Bits = Vcb->LBlockSizeBits - Vcb->BlockSizeBits;
    UDFPrint(("UDF: mapTableLength = %x\n", 0));
    UDFPrint(("UDF: numPartitionMaps = %x\n", 0));

// if(CDRW) {

    Vcb->Partitions[0].PartitionType = UDF_TYPE1_MAP15;
    Vcb->Partitions[0].VolumeSeqNum = 0;
    Vcb->Partitions[0].PartitionNum = 0;

/* } else if(CDR)
                if()
                    UDFPrint(("Found VAT 1.50\n"));
                    Vcb->Partitions[i].PartitionType = UDF_VIRTUAL_MAP15;
                } else
                    UDFPrint(("Found VAT 2.00\n"));
                    Vcb->Partitions[i].PartitionType = UDF_VIRTUAL_MAP20;
                }
            }
    }
*/
    if(fileset) {
        // remember FileSet location
//        long_ad *la = (long_ad *)&(lvd->logicalVolContentsUse[0]);
        fileset->logicalBlockNum = 0;
        fileset->partitionReferenceNum = 0;
        UDFPrint(("FileSet found in LogicalVolDesc at block=%x, partition=%d\n",
            fileset->logicalBlockNum,
            fileset->partitionReferenceNum));
    }
    return STATUS_SUCCESS;
} // end UDFLoadBogusLogicalVol()

/*
    This routine adds given Bitmap to existing one
 */
OSSTATUS
UDFAddXSpaceBitmap(
    IN PVCB Vcb,
    IN uint32 PartNum,
    IN PSHORT_AD bm,
    IN ULONG bm_type
    )
{
    int8* tmp;
    int8* tmp_bm;
    uint32 i, lim, j, lba, l, lim2, l2, k;
    lb_addr locAddr;
    OSSTATUS status;
    uint16 Ident;
    uint32 flags;
    SIZE_T Length;
    SIZE_T ReadBytes;
    BOOLEAN bit_set;

    UDF_CHECK_BITMAP_RESOURCE(Vcb);
    UDFPrint(("UDFAddXSpaceBitmap: at block=%x, partition=%d\n",
        bm->extPosition,
        PartNum));

    if(!(Length = (bm->extLength & UDF_EXTENT_LENGTH_MASK))) return STATUS_SUCCESS;
    i=UDFPartStart(Vcb, PartNum);
    flags = bm->extLength >> 30;
    if(!flags /*|| flags == EXTENT_NOT_RECORDED_ALLOCATED*/) {
        tmp = (int8*)DbgAllocatePool(NonPagedPool, max(Length, Vcb->BlockSize));
        if(!tmp) return STATUS_INSUFFICIENT_RESOURCES;
        locAddr.partitionReferenceNum = (uint16)PartNum;
        locAddr.logicalBlockNum = bm->extPosition;
        // read header of the Bitmap
        if(!OS_SUCCESS(status = UDFReadTagged(Vcb, tmp, lba = UDFPartLbaToPhys(Vcb, &(locAddr)),
                             locAddr.logicalBlockNum, &Ident)) ) {
err_addxsbm_1:
            DbgFreePool(tmp);
            return status;
        }
        if(Ident != TID_SPACE_BITMAP_DESC) {
            status = STATUS_DISK_CORRUPT_ERROR;
            goto err_addxsbm_1;
        }
        UDFRegisterFsStructure(Vcb, lba, Vcb->BlockSize);
        // read the whole Bitmap
        if(!OS_SUCCESS(status = UDFReadData(Vcb, FALSE, ((uint64)lba)<<Vcb->BlockSizeBits, Length, FALSE, tmp, &ReadBytes)))
            goto err_addxsbm_1;
        UDFRegisterFsStructure(Vcb, lba, Length);
        lim = min(i + ((lim2 = ((PSPACE_BITMAP_DESC)tmp)->numOfBits) << Vcb->LB2B_Bits), Vcb->FSBM_BitCount);
        tmp_bm = tmp + sizeof(SPACE_BITMAP_DESC);
        j = 0;
        for(;(l = UDFGetBitmapLen((uint32*)tmp_bm, j, lim2)) && (i<lim);) {
            // expand LBlocks to Sectors...
            l2 = l << Vcb->LB2B_Bits;
            // ...and mark them
            if(bm_type == UDF_FSPACE_BM) {
                bit_set = UDFGetFreeBit(tmp_bm, j);
                for(k=0;(k<l2) && (i<lim);k++) {
                    if(bit_set) {
                        // FREE block
                        UDFSetFreeBit(Vcb->FSBM_Bitmap, i);
                        UDFSetFreeBitOwner(Vcb, i);
                        UDFSetZeroBit(Vcb->ZSBM_Bitmap, i);
                    } else {
                        // USED block
                        UDFClrZeroBit(Vcb->ZSBM_Bitmap, i);
                    }
                    i++;
                }
            } else {
                bit_set = UDFGetZeroBit(tmp_bm, j);
                for(k=0;(k<l2) && (i<lim);k++) {
                    if(bit_set) {
                        // ZERO block
                        UDFSetZeroBit(Vcb->ZSBM_Bitmap, i);
                    } else {
                        // DATA block
                        UDFClrZeroBit(Vcb->ZSBM_Bitmap, i);
                    }
                    i++;
                }
            }
            j += l;
        }
        DbgFreePool(tmp);
/*    } else if((bm->extLength >> 30) == EXTENT_NOT_RECORDED_ALLOCATED) {
        i=Vcb->Partitions[PartNum].PartitionRoot;
        lim = i + Vcb->Partitions[PartNum].PartitionLen;
        for(;i<lim;i++) {
            UDFSetUsedBit(Vcb->FSBM_Bitmap, i);
        }*/
    }
    return STATUS_SUCCESS;
} // end UDFAddXSpaceBitmap()

/*
    This routine adds given Bitmap to existing one
 */
OSSTATUS
UDFVerifyXSpaceBitmap(
    IN PVCB Vcb,
    IN uint32 PartNum,
    IN PSHORT_AD bm,
    IN ULONG bm_type
    )
{
    int8* tmp;
//    int8* tmp_bm;
//    uint32 i, l2, k, lim, j, lim2;
    uint32 lba;
    lb_addr locAddr;
    OSSTATUS status;
    uint16 Ident;
    uint32 flags;
    uint32 Length;
    SIZE_T ReadBytes;
//    BOOLEAN bit_set;

    UDF_CHECK_BITMAP_RESOURCE(Vcb);

    UDFPrint((" UDFVerifyXSpaceBitmap: part %x\n", PartNum));

    if(!(Length = (bm->extLength & UDF_EXTENT_LENGTH_MASK))) return STATUS_SUCCESS;
//    i=UDFPartStart(Vcb, PartNum);
    flags = bm->extLength >> 30;
    if(!flags /*|| flags == EXTENT_NOT_RECORDED_ALLOCATED*/) {
        tmp = (int8*)DbgAllocatePool(NonPagedPool, max(Length, Vcb->BlockSize));
        if(!tmp) return STATUS_INSUFFICIENT_RESOURCES;
        locAddr.partitionReferenceNum = (uint16)PartNum;
        locAddr.logicalBlockNum = bm->extPosition;
        // read header of the Bitmap
        if(!OS_SUCCESS(status = UDFReadTagged(Vcb, tmp, lba = UDFPartLbaToPhys(Vcb, &(locAddr)),
                             locAddr.logicalBlockNum, &Ident)) ) {
err_vfyxsbm_1:
            DbgFreePool(tmp);
            return status;
        }
        UDFPrint((" BM Lba %x\n", lba));
        if(Ident != TID_SPACE_BITMAP_DESC) {
            status = STATUS_DISK_CORRUPT_ERROR;
            goto err_vfyxsbm_1;
        }
        // read the whole Bitmap
        if(!OS_SUCCESS(status = UDFReadData(Vcb, FALSE, ((uint64)lba)<<Vcb->BlockSizeBits, Length, FALSE, tmp, &ReadBytes)))
            goto err_vfyxsbm_1;
        UDFRegisterFsStructure(Vcb, lba, Length);
//        lim = min(i + ((lim2 = ((PSPACE_BITMAP_DESC)tmp)->numOfBits) << Vcb->LB2B_Bits), Vcb->FSBM_BitCount);
//        tmp_bm = tmp + sizeof(SPACE_BITMAP_DESC);
//        j = 0;
/*        for(;(l = UDFGetBitmapLen((uint32*)tmp_bm, j, lim2)) && (i<lim);) {
            // expand LBlocks to Sectors...
            l2 = l << Vcb->LB2B_Bits;
            // ...and mark them
            if(bm_type == UDF_FSPACE_BM) {
                bit_set = UDFGetFreeBit(tmp_bm, j);
                for(k=0;(k<l2) && (i<lim);k++) {
                    if(bit_set) {
                        // FREE block
                        UDFSetFreeBit(Vcb->FSBM_Bitmap, i);
                        UDFSetFreeBitOwner(Vcb, i);
                        UDFSetZeroBit(Vcb->ZSBM_Bitmap, i);
                    } else {
                        // USED block
                        UDFClrZeroBit(Vcb->ZSBM_Bitmap, i);
                    }
                    i++;
                }
            } else {
                bit_set = UDFGetZeroBit(tmp_bm, j);
                for(k=0;(k<l2) && (i<lim);k++) {
                    if(bit_set) {
                        // ZERO block
                        UDFSetZeroBit(Vcb->ZSBM_Bitmap, i);
                    } else {
                        // DATA block
                        UDFClrZeroBit(Vcb->ZSBM_Bitmap, i);
                    }
                    i++;
                }
            }
            j += l;
        }*/
        DbgFreePool(tmp);
/*    } else if((bm->extLength >> 30) == EXTENT_NOT_RECORDED_ALLOCATED) {
        i=Vcb->Partitions[PartNum].PartitionRoot;
        lim = i + Vcb->Partitions[PartNum].PartitionLen;
        for(;i<lim;i++) {
            UDFSetUsedBit(Vcb->FSBM_Bitmap, i);
        }*/
    }
    return STATUS_SUCCESS;
} // end UDFVerifyXSpaceBitmap()

/*
    This routine subtracts given Bitmap to existing one
 */
/*OSSTATUS
UDFDelXSpaceBitmap(
    IN PVCB Vcb,
    IN uint32 PartNum,
    IN PSHORT_AD bm
    )
{
    int8* tmp, tmp_bm;
    uint32 i, lim, j;
    lb_addr locAddr;
    OSSTATUS status;
    uint16 Ident;
    uint32 flags;
    uint32 Length;
    SIZE_T ReadBytes;

    if(!(Length = (bm->extLength & UDF_EXTENT_LENGTH_MASK))) return STATUS_SUCCESS;
    i=0;
    flags = bm->extLength >> 30;
    if(!flags || flags == EXTENT_NOT_RECORDED_ALLOCATED) {
        tmp = (int8*)MyAllocatePool__(NonPagedPool, Length);
        if(!tmp) return STATUS_INSUFFICIENT_RESOURCES;
        locAddr.partitionReferenceNum = (uint16)PartNum;
        locAddr.logicalBlockNum = bm->extPosition;
        if((!OS_SUCCESS(status = UDFReadTagged(Vcb, tmp, (j = UDFPartLbaToPhys(Vcb, &(locAddr))),
                             locAddr.logicalBlockNum, &Ident))) ||
           (Ident != TID_SPACE_BITMAP_DESC) ) {
            MyFreePool__(tmp);
            return status;
        }
        if(!OS_SUCCESS(status = UDFReadData(Vcb, FALSE, ((uint64)j)<<Vcb->BlockSizeBits, Length, FALSE, tmp, &ReadBytes))) {
            MyFreePool__(tmp);
            return status;
        }
        lim = i + ((PSPACE_BITMAP_DESC)tmp)->numOfBits;
        tmp_bm = tmp + sizeof(SPACE_BITMAP_DESC);
        j = 0;
        for(;i<lim;i++) {
            if(UDFGetUsedBit(tmp_bm, j)) UDFSetFreeBit(Vcb->FSBM_Bitmap, i);
            j++;
        }
        MyFreePool__(tmp);
//    } else if((bm->extLength >> 30) == EXTENT_NOT_RECORDED_ALLOCATED) {
//        i=Vcb->Partitions[PartNum].PartitionRoot;
//        lim = i + Vcb->Partitions[PartNum].PartitionLen;
//        for(;i<lim;i++) {
//            UDFSetUsedBit(Vcb->FSBM_Bitmap, i);
//        }
    }
    return STATUS_SUCCESS;
} // end UDFDelXSpaceBitmap()  */

/*
    This routine verifues FreeSpaceBitmap (internal) according to media
    parameters & input data
 */
OSSTATUS
UDFVerifyFreeSpaceBitmap(
    IN PVCB Vcb,
    IN uint32 PartNdx,
    IN PPARTITION_HEADER_DESC phd, // partition header pointing to Bitmaps
    IN uint32 Lba                   // UnallocSpaceDesc
    )
{
    OSSTATUS status;
    uint32 i, l;
    uint16 Ident;
    int8* AllocDesc;
    PEXTENT_MAP Extent;
    lb_addr locAddr;
    uint32 PartNum;

    PartNum = UDFGetPartNumByPartNdx(Vcb, PartNdx);

    UDFPrint(("UDFVerifyFreeSpaceBitmap:\n"));
    // read info for partition header (if any)
    if(phd) {
        // read unallocated Bitmap
        if(!OS_SUCCESS(status = UDFVerifyXSpaceBitmap(Vcb, PartNum, &(phd->unallocatedSpaceBitmap), UDF_FSPACE_BM)))
            return status;
        // read freed Bitmap
        if(!OS_SUCCESS(status = UDFVerifyXSpaceBitmap(Vcb, PartNum, &(phd->freedSpaceBitmap), UDF_ZSPACE_BM)))
            return status;
    }
    // read UnallocatedSpaceDesc & convert to Bitmap
    if(Lba) {
        UDFPrint((" Lba @%x\n", Lba));
        if(!(AllocDesc = (int8*)MyAllocatePool__(NonPagedPool, Vcb->LBlockSize + sizeof(EXTENT_AD) )))
            return STATUS_INSUFFICIENT_RESOURCES;
        RtlZeroMemory(((int8*)AllocDesc) + Vcb->LBlockSize, sizeof(EXTENT_AD));
        if(!OS_SUCCESS(status = UDFReadTagged(Vcb, AllocDesc, Lba, Lba, &Ident)) ||
           !(Extent = (PEXTENT_MAP)MyAllocatePool__(NonPagedPool, l = (((PUNALLOC_SPACE_DESC)AllocDesc)->numAllocDescs+1) * sizeof(EXTENT_AD) ))) {
            MyFreePool__(AllocDesc);
            return status;
        }
        UDFRegisterFsStructure(Vcb, Lba, Vcb->BlockSize);
        RtlCopyMemory((int8*)Extent, AllocDesc+sizeof(UNALLOC_SPACE_DESC), (((PUNALLOC_SPACE_DESC)AllocDesc)->numAllocDescs+1) * sizeof(EXTENT_AD) );
        locAddr.partitionReferenceNum = (uint16)PartNum;
        // read extent is recorded with relative addresses
        // so, we should convert it to suitable form
        for(i=0; Extent[i].extLength; i++) {
            locAddr.logicalBlockNum = Extent[i].extLocation;
            Extent[i].extLocation = UDFPartLbaToPhys(Vcb, &locAddr);
            if(Extent[i].extLocation == LBA_OUT_OF_EXTENT) {
                BrutePoint();
                MyFreePool__(AllocDesc);
                return STATUS_DISK_CORRUPT_ERROR;
            }
            if((Extent[i].extLocation >> 30) == EXTENT_NEXT_EXTENT_ALLOCDESC) {
                // load continuation
                Lba = Extent[i].extLocation & UDF_EXTENT_LENGTH_MASK;
                if(!OS_SUCCESS(status = UDFReadTagged(Vcb, AllocDesc, Lba, Lba, &Ident)) ||
                   !(Extent = (PEXTENT_MAP)MyAllocatePool__(NonPagedPool, (((PUNALLOC_SPACE_DESC)AllocDesc)->numAllocDescs+1) * sizeof(EXTENT_AD) ))) {
                    MyFreePool__(AllocDesc);
                    return status;
                }
                if(Ident == TID_UNALLOC_SPACE_DESC) {
                    UDFRegisterFsStructure(Vcb, Lba, Vcb->BlockSize);
                    if(!(l = MyReallocPool__((int8*)Extent, l, (int8**)&Extent, i*sizeof(EXTENT_MAP)))) {
                        MyFreePool__(Extent);
                        MyFreePool__(AllocDesc);
                        return STATUS_INSUFFICIENT_RESOURCES;
                    }
                    Extent[i].extLength =
                    Extent[i].extLocation = 0;
                    Extent = UDFMergeMappings(Extent, (PEXTENT_MAP)(AllocDesc+sizeof(UNALLOC_SPACE_DESC)) );
#ifdef UDF_DBG
                } else {
                    UDFPrint(("Broken unallocated space descriptor sequence\n"));
#endif // UDF_DBG
                }
            }
        }
//        UDFMarkSpaceAsXXX(Vcb, (-1), Extent, AS_USED); // mark as used
        MyFreePool__(Extent);
        MyFreePool__(AllocDesc);
        status = STATUS_SUCCESS;
    }
    return status;
} // end UDFBuildFreeSpaceBitmap()

/*
    This routine builds FreeSpaceBitmap (internal) according to media
    parameters & input data
 */
OSSTATUS
UDFBuildFreeSpaceBitmap(
    IN PVCB Vcb,
    IN uint32 PartNdx,
    IN PPARTITION_HEADER_DESC phd, // partition header pointing to Bitmaps
    IN uint32 Lba                   // UnallocSpaceDesc
    )
{
    OSSTATUS status;
    uint32 i, l;
    uint16 Ident;
    int8* AllocDesc;
    PEXTENT_MAP Extent;
    lb_addr locAddr;
    uint32 PartNum;

    PartNum = UDFGetPartNumByPartNdx(Vcb, PartNdx);
    if(!(Vcb->FSBM_Bitmap)) {
        // init Bitmap buffer if necessary
        Vcb->FSBM_Bitmap = (int8*)DbgAllocatePool(NonPagedPool, (i = (Vcb->LastPossibleLBA+1+7)>>3) );
        if(!(Vcb->FSBM_Bitmap)) return STATUS_INSUFFICIENT_RESOURCES;

        Vcb->ZSBM_Bitmap = (int8*)DbgAllocatePool(NonPagedPool, (i = (Vcb->LastPossibleLBA+1+7)>>3) );
        if(!(Vcb->ZSBM_Bitmap)) {
#ifdef UDF_TRACK_ONDISK_ALLOCATION_OWNERS
free_fsbm:
#endif //UDF_TRACK_ONDISK_ALLOCATION_OWNERS
            MyFreePool__(Vcb->FSBM_Bitmap);
            Vcb->FSBM_Bitmap = NULL;
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        RtlZeroMemory(Vcb->FSBM_Bitmap, i);
        RtlZeroMemory(Vcb->ZSBM_Bitmap, i);
#ifdef UDF_TRACK_ONDISK_ALLOCATION_OWNERS
        Vcb->FSBM_Bitmap_owners = (uint32*)DbgAllocatePool(NonPagedPool, (Vcb->LastPossibleLBA+1)*sizeof(uint32));
        if(!(Vcb->FSBM_Bitmap_owners)) {
            MyFreePool__(Vcb->ZSBM_Bitmap);
            Vcb->ZSBM_Bitmap = NULL;
            goto free_fsbm;
        }
        RtlFillMemory(Vcb->FSBM_Bitmap_owners, (Vcb->LastPossibleLBA+1)*sizeof(uint32), 0xff);
#endif //UDF_TRACK_ONDISK_ALLOCATION_OWNERS
        Vcb->FSBM_ByteCount = i;
        Vcb->FSBM_BitCount = Vcb->LastPossibleLBA+1;
    }
    // read info for partition header (if any)
    if(phd) {
        // read unallocated Bitmap
        if(!OS_SUCCESS(status = UDFAddXSpaceBitmap(Vcb, PartNum, &(phd->unallocatedSpaceBitmap), UDF_FSPACE_BM)))
            return status;
        // read freed Bitmap
        if(!OS_SUCCESS(status = UDFAddXSpaceBitmap(Vcb, PartNum, &(phd->freedSpaceBitmap), UDF_ZSPACE_BM)))
            return status;
    }
    // read UnallocatedSpaceDesc & convert to Bitmap
    if(Lba) {
        if(!(AllocDesc = (int8*)MyAllocatePool__(NonPagedPool, Vcb->LBlockSize + sizeof(EXTENT_AD) )))
            return STATUS_INSUFFICIENT_RESOURCES;
        RtlZeroMemory(((int8*)AllocDesc) + Vcb->LBlockSize, sizeof(EXTENT_AD));
        if(!OS_SUCCESS(status = UDFReadTagged(Vcb, AllocDesc, Lba, Lba, &Ident)) ||
           !(Extent = (PEXTENT_MAP)MyAllocatePool__(NonPagedPool, l = (((PUNALLOC_SPACE_DESC)AllocDesc)->numAllocDescs+1) * sizeof(EXTENT_AD) ))) {
            MyFreePool__(AllocDesc);
            return status;
        }
        UDFRegisterFsStructure(Vcb, Lba, Vcb->BlockSize);
        RtlCopyMemory((int8*)Extent, AllocDesc+sizeof(UNALLOC_SPACE_DESC), (((PUNALLOC_SPACE_DESC)AllocDesc)->numAllocDescs+1) * sizeof(EXTENT_AD) );
        locAddr.partitionReferenceNum = (uint16)PartNum;
        // read extent is recorded with relative addresses
        // so, we should convert it to suitable form
        for(i=0; Extent[i].extLength; i++) {
            locAddr.logicalBlockNum = Extent[i].extLocation;
            Extent[i].extLocation = UDFPartLbaToPhys(Vcb, &locAddr);
            if(Extent[i].extLocation == LBA_OUT_OF_EXTENT) {
                BrutePoint();
                MyFreePool__(AllocDesc);
                return STATUS_DISK_CORRUPT_ERROR;
            }
            if((Extent[i].extLocation >> 30) == EXTENT_NEXT_EXTENT_ALLOCDESC) {
                // load continuation
                Lba = Extent[i].extLocation & UDF_EXTENT_LENGTH_MASK;
                if(!OS_SUCCESS(status = UDFReadTagged(Vcb, AllocDesc, Lba, Lba, &Ident)) ||
                   !(Extent = (PEXTENT_MAP)MyAllocatePool__(NonPagedPool, (((PUNALLOC_SPACE_DESC)AllocDesc)->numAllocDescs+1) * sizeof(EXTENT_AD) ))) {
                    MyFreePool__(AllocDesc);
                    return status;
                }
                if(Ident == TID_UNALLOC_SPACE_DESC) {
                    UDFRegisterFsStructure(Vcb, Lba, Vcb->BlockSize);
                    if(!(l = MyReallocPool__((int8*)Extent, l, (int8**)&Extent, i*sizeof(EXTENT_MAP)))) {
                        MyFreePool__(Extent);
                        MyFreePool__(AllocDesc);
                        return STATUS_INSUFFICIENT_RESOURCES;
                    }
                    Extent[i].extLength =
                    Extent[i].extLocation = 0;
                    Extent = UDFMergeMappings(Extent, (PEXTENT_MAP)(AllocDesc+sizeof(UNALLOC_SPACE_DESC)) );
#ifdef UDF_DBG
                } else {
                    UDFPrint(("Broken unallocated space descriptor sequence\n"));
#endif // UDF_DBG
                }
            }
        }
        UDFMarkSpaceAsXXX(Vcb, (-1), Extent, AS_USED); // mark as used
        MyFreePool__(Extent);
        MyFreePool__(AllocDesc);
    }
    return status;
} // end UDFVerifyFreeSpaceBitmap()

/*
    process Partition descriptor
 */
OSSTATUS
UDFLoadPartDesc(
    PVCB      Vcb,
    int8*     Buf
    )
{
    PartitionDesc *p = (PartitionDesc *)Buf;
    uint32 i;
    OSSTATUS RC;
    BOOLEAN Found = FALSE;
    UDFPrint(("UDF: Pard Descr:\n"));
    UDFPrint((" volDescSeqNum   = %x\n", p->volDescSeqNum));
    UDFPrint((" partitionFlags  = %x\n", p->partitionFlags));
    UDFPrint((" partitionNumber = %x\n", p->partitionNumber));
    UDFPrint((" accessType      = %x\n", p->accessType));
    UDFPrint((" partitionStartingLocation = %x\n", p->partitionStartingLocation));
    UDFPrint((" partitionLength = %x\n", p->partitionLength));
    // There is nothing interesting to comment here
    // Just look at Names & Messages....
    for (i=0; i<Vcb->PartitionMaps; i++) {
        UDFPrint(("Searching map: (%d == %d)\n",
            Vcb->Partitions[i].PartitionNum, (p->partitionNumber) ));
        if(Vcb->Partitions[i].PartitionNum == (p->partitionNumber)) {
            Found = TRUE;
            Vcb->Partitions[i].PartitionRoot = p->partitionStartingLocation + Vcb->FirstLBA;
            Vcb->Partitions[i].PartitionLen = p->partitionLength;
            Vcb->Partitions[i].UspaceBitmap = 0xFFFFFFFF;
            Vcb->Partitions[i].FspaceBitmap = 0xFFFFFFFF;
            Vcb->Partitions[i].AccessType = p->accessType;
            UDFPrint(("Access mode %x\n", p->accessType));
            if(p->accessType == PARTITION_ACCESS_WO) {
                Vcb->CDR_Mode = TRUE;
//                Vcb->Partitions[i].PartitionLen = Vcb->LastPossibleLBA - p->partitionStartingLocation;
            } else if(p->accessType < PARTITION_ACCESS_WO) {
                // Soft-read-only volume
                UDFPrint(("Soft Read-only volume\n"));
                Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
                Vcb->UserFSFlags |= UDF_USER_FS_FLAGS_PART_RO;
            } else if(p->accessType > PARTITION_ACCESS_MAX_KNOWN) {
                return STATUS_UNRECOGNIZED_MEDIA;
            }

            if(!strcmp((int8*)&(p->partitionContents.ident), PARTITION_CONTENTS_NSR02) ||
                !strcmp((int8*)&(p->partitionContents.ident), PARTITION_CONTENTS_NSR03))
            {
                PPARTITION_HEADER_DESC phd;

                phd = (PPARTITION_HEADER_DESC)(p->partitionContentsUse);
#ifdef UDF_DBG
                if(phd->unallocatedSpaceTable.extLength)
                    UDFPrint(("unallocatedSpaceTable (part %d)\n", i));
#endif // UDF_DBG
                if(phd->unallocatedSpaceBitmap.extLength) {
                    Vcb->Partitions[i].UspaceBitmap =
                        phd->unallocatedSpaceBitmap.extPosition;
                    UDFPrint(("unallocatedSpaceBitmap (part %d) @ %x\n",
                        i, Vcb->Partitions[i].UspaceBitmap ));
                }
#ifdef UDF_DBG
                if(phd->partitionIntegrityTable.extLength)
                    UDFPrint(("partitionIntegrityTable (part %d)\n", i));
                if(phd->freedSpaceTable.extLength)
                    UDFPrint(("freedSpaceTable (part %d)\n", i));
#endif // UDF_DBG
                if(phd->freedSpaceBitmap.extLength) {
                    Vcb->Partitions[i].FspaceBitmap =
                        phd->freedSpaceBitmap.extPosition;
                    UDFPrint(("freedSpaceBitmap (part %d)\n", i));
                }
                RC = UDFBuildFreeSpaceBitmap(Vcb, i, phd, 0);
                //Vcb->Modified = FALSE;
                UDFPreClrModified(Vcb);
                UDFClrModified(Vcb);
                if(!OS_SUCCESS(RC))
                    return RC;

                if ((Vcb->Partitions[i].PartitionType == UDF_VIRTUAL_MAP15) ||
                    (Vcb->Partitions[i].PartitionType == UDF_VIRTUAL_MAP20)) {
                    RC = UDFLoadVAT(Vcb, i);
                    if(!OS_SUCCESS(RC))
                        return RC;
                    WCacheFlushAll__(&(Vcb->FastCache), Vcb);
                    WCacheSetMode__(&(Vcb->FastCache), WCACHE_MODE_R);
                    Vcb->LastModifiedTrack = 0;
                }
            }
        }
    }
#ifdef UDF_DBG
    if(!Found) {
        UDFPrint(("Partition (%d) not found in partition map\n", (p->partitionNumber) ));
    } else {
        UDFPrint(("Partition (%d:%d type %x) starts at physical %x, length %x\n",
            p->partitionNumber, i-1, Vcb->Partitions[i-1].PartitionType,
            Vcb->Partitions[i-1].PartitionRoot, Vcb->Partitions[i-1].PartitionLen));
    }
#endif // UDF_DBG
    return STATUS_SUCCESS;
} // end UDFLoadPartDesc()

/*
    process Partition descriptor
 */
OSSTATUS
UDFVerifyPartDesc(
    PVCB      Vcb,
    int8*     Buf
    )
{
    PartitionDesc *p = (PartitionDesc *)Buf;
    uint32 i;
    OSSTATUS RC;
    BOOLEAN Found = FALSE;
    UDFPrint(("UDF: Verify Part Descr:\n"));
    UDFPrint((" volDescSeqNum   = %x\n", p->volDescSeqNum));
    UDFPrint((" partitionFlags  = %x\n", p->partitionFlags));
    UDFPrint((" partitionNumber = %x\n", p->partitionNumber));
    UDFPrint((" accessType      = %x\n", p->accessType));
    UDFPrint((" partitionStartingLocation = %x\n", p->partitionStartingLocation));
    UDFPrint((" partitionLength = %x\n", p->partitionLength));
    // There is nothing interesting to comment here
    // Just look at Names & Messages....
    for (i=0; i<Vcb->PartitionMaps; i++) {
        UDFPrint(("Searching map: (%d == %d)\n",
            Vcb->Partitions[i].PartitionNum, (p->partitionNumber) ));
        if(Vcb->Partitions[i].PartitionNum == (p->partitionNumber)) {
            Found = TRUE;
            if(Vcb->Partitions[i].PartitionRoot != p->partitionStartingLocation + Vcb->FirstLBA)
                return STATUS_DISK_CORRUPT_ERROR;
            if(Vcb->Partitions[i].PartitionLen !=
                min(p->partitionLength,
                    Vcb->LastPossibleLBA - Vcb->Partitions[i].PartitionRoot)) /* sectors */
                return STATUS_DISK_CORRUPT_ERROR;
//            Vcb->Partitions[i].UspaceBitmap = 0xFFFFFFFF;
//            Vcb->Partitions[i].FspaceBitmap = 0xFFFFFFFF;
            if(Vcb->Partitions[i].AccessType != p->accessType)
                return STATUS_DISK_CORRUPT_ERROR;
            UDFPrint(("Access mode %x\n", p->accessType));
            if(p->accessType == PARTITION_ACCESS_WO) {
                if(Vcb->CDR_Mode != TRUE)
                    return STATUS_DISK_CORRUPT_ERROR;
//                Vcb->Partitions[i].PartitionLen = Vcb->LastPossibleLBA - p->partitionStartingLocation;
            } else if(p->accessType < PARTITION_ACCESS_WO) {
                // Soft-read-only volume
                UDFPrint(("Soft Read-only volume\n"));
                if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY))
                    return STATUS_DISK_CORRUPT_ERROR;
            } else if(p->accessType > PARTITION_ACCESS_MAX_KNOWN) {
                return STATUS_UNRECOGNIZED_MEDIA;
            }

            if(!strcmp((int8*)&(p->partitionContents.ident), PARTITION_CONTENTS_NSR02) ||
                !strcmp((int8*)&(p->partitionContents.ident), PARTITION_CONTENTS_NSR03))
            {
                PPARTITION_HEADER_DESC phd;

                phd = (PPARTITION_HEADER_DESC)(p->partitionContentsUse);
#ifdef UDF_DBG
                if(phd->unallocatedSpaceTable.extLength)
                    UDFPrint(("unallocatedSpaceTable (part %d)\n", i));
#endif // UDF_DBG
                if(phd->unallocatedSpaceBitmap.extLength) {
                    if(Vcb->Partitions[i].UspaceBitmap ==
                        phd->unallocatedSpaceBitmap.extPosition) {
                        UDFPrint(("Warning: both USpaceBitmaps have same location\n"));
                    }
                    UDFPrint(("unallocatedSpaceBitmap (part %d) @ %x\n",
                        i, Vcb->Partitions[i].UspaceBitmap ));
                }
#ifdef UDF_DBG
                if(phd->partitionIntegrityTable.extLength)
                    UDFPrint(("partitionIntegrityTable (part %d)\n", i));
                if(phd->freedSpaceTable.extLength)
                    UDFPrint(("freedSpaceTable (part %d)\n", i));
#endif // UDF_DBG
                if(phd->freedSpaceBitmap.extLength) {
                    if(Vcb->Partitions[i].FspaceBitmap ==
                        phd->freedSpaceBitmap.extPosition) {
                        UDFPrint(("Warning: both FSpaceBitmaps have same location\n"));
                    }
                    UDFPrint(("freedSpaceBitmap (part %d)\n", i));
                }
                RC = UDFVerifyFreeSpaceBitmap(Vcb, i, phd, 0);
                //Vcb->Modified = FALSE;
                //UDFPreClrModified(Vcb);
                //UDFClrModified(Vcb);
                if(!OS_SUCCESS(RC))
                    return RC;

                if ((Vcb->Partitions[i].PartitionType == UDF_VIRTUAL_MAP15) ||
                    (Vcb->Partitions[i].PartitionType == UDF_VIRTUAL_MAP20)) {
/*                    RC = UDFLoadVAT(Vcb, i);
                    if(!OS_SUCCESS(RC))
                        return RC;
                    WCacheFlushAll__(&(Vcb->FastCache), Vcb);
                    WCacheSetMode__(&(Vcb->FastCache), WCACHE_MODE_R);
                    Vcb->LastModifiedTrack = 0;*/
                }
            }
        }
    }
#ifdef UDF_DBG
    if(!Found) {
        UDFPrint(("Partition (%d) not found in partition map\n", (p->partitionNumber) ));
    } else {
        UDFPrint(("Partition (%d:%d type %x) starts at physical %x, length %x\n",
            p->partitionNumber, i-1, Vcb->Partitions[i-1].PartitionType,
            Vcb->Partitions[i-1].PartitionRoot, Vcb->Partitions[i-1].PartitionLen));
    }
#endif // UDF_DBG
    return STATUS_SUCCESS;
} // end UDFVerifyPartDesc()

/*
    This routine scans VDS & fills special array with Desc locations
 */
OSSTATUS
UDFReadVDS(
    IN PVCB Vcb,
    IN uint32 block,
    IN uint32 lastblock,
    IN PUDF_VDS_RECORD vds,
    IN int8* Buf
    )
{
    OSSTATUS status;
    GenericDesc* gd;
    BOOLEAN done=FALSE;
    uint32 vdsn;
    uint16 ident;

    UDFPrint(("UDF: Read VDS (%x - %x)\n", block, lastblock ));
    // Read the main descriptor sequence
    for (;(!done && block <= lastblock); block++)
    {
        status = UDFReadTagged(Vcb, Buf, block, block, &ident);
        if(!OS_SUCCESS(status))
            return status;
        UDFRegisterFsStructure(Vcb, block, Vcb->BlockSize);

        // Process each descriptor (ISO 13346 3/8.3-8.4)
        gd = (struct GenericDesc *)Buf;
        vdsn = gd->volDescSeqNum;
        UDFPrint(("LBA %x, Ident = %x, vdsn = %x\n", block, ident, vdsn ));
        switch (ident)
        {
            case TID_PRIMARY_VOL_DESC: // ISO 13346 3/10.1
                if(vdsn >= vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum)
                {
                    vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum = vdsn;
                    vds[VDS_POS_PRIMARY_VOL_DESC].block = block;
                }
                break;
            case TID_VOL_DESC_PTR: // ISO 13346 3/10.3
                struct VolDescPtr* pVDP;
                if(vdsn >= vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum)
                {
                    vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum = vdsn;
                    vds[VDS_POS_VOL_DESC_PTR].block = block;
                    vds[VDS_POS_RECURSION_COUNTER].volDescSeqNum++;
                    if(vds[VDS_POS_RECURSION_COUNTER].volDescSeqNum > MAX_VDS_PARTS) {
                       UDFPrint(("too long multipart VDS -> abort\n"));
                        return STATUS_DISK_CORRUPT_ERROR;
                    }
                    pVDP = (struct VolDescPtr*)Buf;
                    UDFPrint(("multipart VDS...\n"));
                    return UDFReadVDS(Vcb, pVDP->nextVolDescSeqExt.extLocation,
                                         pVDP->nextVolDescSeqExt.extLocation + (pVDP->nextVolDescSeqExt.extLocation >> Vcb->BlockSizeBits),
                                         vds, Buf);
                }
                break;
            case TID_IMP_USE_VOL_DESC: // ISO 13346 3/10.4
                if(vdsn >= vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum)
                {
                    vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum = vdsn;
                    vds[VDS_POS_IMP_USE_VOL_DESC].block = block;
                }
                break;
            case TID_PARTITION_DESC: // ISO 13346 3/10.5
                if(!vds[VDS_POS_PARTITION_DESC].block)
                    vds[VDS_POS_PARTITION_DESC].block = block;
                break;
            case TID_LOGICAL_VOL_DESC: // ISO 13346 3/10.6
            case TID_ADAPTEC_LOGICAL_VOL_DESC: // Adaptec Compressed UDF extesion
                if(vdsn >= vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum)
                {
                    vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum = vdsn;
                    vds[VDS_POS_LOGICAL_VOL_DESC].block = block;
                }
                break;
            case TID_UNALLOC_SPACE_DESC: // ISO 13346 3/10.8
                if(vdsn >= vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum)
                {
                    vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum = vdsn;
                    vds[VDS_POS_UNALLOC_SPACE_DESC].block = block;
                }
                break;
            case TID_TERMINATING_DESC: // ISO 13346 3/10.9
                vds[VDS_POS_TERMINATING_DESC].block = block;
                done = TRUE;
                break;
        }
    }
    return STATUS_SUCCESS;
} // UDFReadVDS()

OSSTATUS
UDFLoadImpUseVolDesc(
    IN PVCB   Vcb,
    int8*     Buf
    )
{
#ifdef UDF_DBG
    ImpUseVolDesc* iuvd = (ImpUseVolDesc*)Buf;
    ImpUseVolDescImpUse* iuvdiu = (ImpUseVolDescImpUse*)&(iuvd->impUse);
    UDFPrint(("UDF: Imp Use Vol Desc:\n"));
    UDFPrint((" volDescSeqNum = %x\n", iuvd->volDescSeqNum));
    UDFPrint(("UDF: Imp Use Vol Desc Imp Use:\n"));
    KdDump(iuvdiu, sizeof(ImpUseVolDescImpUse));
#endif
    return STATUS_SUCCESS;
} // UDFLoadImpUseVolDesc()

OSSTATUS
UDFLoadUnallocatedSpaceDesc(
    IN PVCB   Vcb,
    int8*     Buf
    )
{
    UDFPrint(("UDF: Unallocated Space Desc:\n"));
//    UnallocatedSpaceDesc* usd = (UnallocatedSpaceDesc*)Buf;
    return STATUS_SUCCESS;
} // UDFLoadImpUseVolDesc()

/*
    Process a main/reserve volume descriptor sequence.
*/
OSSTATUS
UDFProcessSequence(
     IN PDEVICE_OBJECT    DeviceObject,
     IN PVCB              Vcb,
     IN uint32            block,
     IN uint32            lastblock,
    OUT lb_addr           *fileset
    )
{
    OSSTATUS    RC = STATUS_SUCCESS;
    int8*       Buf = (int8*)MyAllocatePool__(NonPagedPool,Vcb->BlockSize);
    UDF_VDS_RECORD vds[VDS_POS_LENGTH];
//    GenericDesc   *gd;
    uint32   i,j;
    uint16  ident;
    int8*  Buf2 = NULL;

    _SEH2_TRY {
        if(!Buf) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
        RtlZeroMemory(vds, sizeof(UDF_VDS_RECORD) * VDS_POS_LENGTH);
        if(!OS_SUCCESS(RC = UDFReadVDS(Vcb, block, lastblock, (PUDF_VDS_RECORD)&vds, Buf)))
            try_return(RC);
        // walk through Vol Desc Sequence according to locations gained by
        // UDFReadVDS() & do some procesing for each one
        // It is very simple dispath routine...
        for (i=0; i<VDS_POS_LENGTH; i++)
        {
            if(vds[i].block)
            {
                if(!OS_SUCCESS(RC = UDFReadTagged(Vcb, Buf, vds[i].block, vds[i].block, &ident)))
                    try_return(RC);
                UDFRegisterFsStructure(Vcb, vds[i].block, Vcb->BlockSize);

                if(i == VDS_POS_PRIMARY_VOL_DESC) {
                    UDFLoadPVolDesc(Vcb,Buf);
                    if(!Vcb->PVolDescAddr.block) {
                        Vcb->PVolDescAddr = vds[i];
                    } else {
                        Vcb->PVolDescAddr2 = vds[i];
                    }
                } else
                if(i == VDS_POS_LOGICAL_VOL_DESC) {
                    RC = UDFLoadLogicalVol(DeviceObject,Vcb, Buf, fileset);
                    if(!OS_SUCCESS(RC)) try_return(RC);
                } else

                if(i == VDS_POS_IMP_USE_VOL_DESC) {
                    UDFLoadImpUseVolDesc(Vcb, Buf);
                } else
                if(i == VDS_POS_UNALLOC_SPACE_DESC) {
                    UDFLoadUnallocatedSpaceDesc(Vcb, Buf);
                } else

                if(i == VDS_POS_PARTITION_DESC)
                {
                    Buf2 = (int8*)MyAllocatePool__(NonPagedPool,Vcb->BlockSize);
                    if(!Buf2) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
                    RC = UDFLoadPartDesc(Vcb,Buf);
                    if(!OS_SUCCESS(RC)) try_return(RC);
                    for (j=vds[i].block+1; j<vds[VDS_POS_TERMINATING_DESC].block; j++)
                    {
                        RC = UDFReadTagged(Vcb,Buf2, j, j, &ident);
                        if(!OS_SUCCESS(RC)) try_return(RC);
                        UDFRegisterFsStructure(Vcb, j, Vcb->BlockSize);
//                        gd = (struct GenericDesc *)Buf2;
                        if(ident == TID_PARTITION_DESC) {
                            RC = UDFLoadPartDesc(Vcb,Buf2);
                            if(!OS_SUCCESS(RC)) try_return(RC);
                        } else if(ident == TID_UNALLOC_SPACE_DESC) {
                            RC = UDFBuildFreeSpaceBitmap(Vcb,0,NULL,j);
                            //Vcb->Modified = FALSE;
                            UDFPreClrModified(Vcb);
                            UDFClrModified(Vcb);
                            if(!OS_SUCCESS(RC))
                                try_return(RC);
                        }
                    }
                    MyFreePool__(Buf2);
                    Buf2 = NULL;
                }
            } else {
                if(i == VDS_POS_LOGICAL_VOL_DESC) {
                    RC = UDFLoadBogusLogicalVol(DeviceObject,Vcb, Buf, fileset);
                    if(!OS_SUCCESS(RC)) try_return(RC);
                }
            }
        }

try_exit: NOTHING;

    } _SEH2_FINALLY {
        if(Buf) MyFreePool__(Buf);
        if(Buf2) MyFreePool__(Buf2);
    } _SEH2_END;

    return RC;
} // end UDFProcessSequence()

/*
    Verifies a main/reserve volume descriptor sequence.
*/
OSSTATUS
UDFVerifySequence(
     IN PDEVICE_OBJECT    DeviceObject,
     IN PVCB              Vcb,
     IN uint32             block,
     IN uint32             lastblock,
     OUT lb_addr          *fileset
     )
{
    OSSTATUS    RC = STATUS_SUCCESS;
    int8*       Buf = (int8*)MyAllocatePool__(NonPagedPool,Vcb->BlockSize);
    UDF_VDS_RECORD vds[VDS_POS_LENGTH];
//    GenericDesc   *gd;
    uint32   i,j;
    uint16  ident;
    int8*  Buf2 = NULL;

    _SEH2_TRY {
        if(!Buf) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
        if(!block) try_return (RC = STATUS_SUCCESS);
        RtlZeroMemory(vds, sizeof(UDF_VDS_RECORD) * VDS_POS_LENGTH);
        if(!OS_SUCCESS(RC = UDFReadVDS(Vcb, block, lastblock, (PUDF_VDS_RECORD)&vds, Buf)))
            try_return(RC);

        for (i=0; i<VDS_POS_LENGTH; i++)
        {
            if(vds[i].block)
            {
                if(!OS_SUCCESS(RC = UDFReadTagged(Vcb, Buf, vds[i].block, vds[i].block, &ident)))
                    try_return(RC);
                UDFRegisterFsStructure(Vcb, vds[i].block, Vcb->BlockSize);

    /*            if(i == VDS_POS_PRIMARY_VOL_DESC)
                    UDFLoadPVolDesc(Vcb,Buf);
                else if(i == VDS_POS_LOGICAL_VOL_DESC) {
                    RC = UDFLoadLogicalVol(DeviceObject,Vcb, Buf, fileset);
                    if(!OS_SUCCESS(RC)) try_return(RC);
                }
                else*/ if(i == VDS_POS_PARTITION_DESC)
                {
                    Buf2 = (int8*)MyAllocatePool__(NonPagedPool,Vcb->BlockSize);
                    if(!Buf2) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
                    RC = UDFVerifyPartDesc(Vcb,Buf);
                    if(!OS_SUCCESS(RC)) try_return(RC);
                    for (j=vds[i].block+1; j<vds[VDS_POS_TERMINATING_DESC].block; j++)
                    {
                        RC = UDFReadTagged(Vcb,Buf2, j, j, &ident);
                        if(!OS_SUCCESS(RC)) try_return(RC);
                        UDFRegisterFsStructure(Vcb, j, Vcb->BlockSize);
//                        gd = (struct GenericDesc *)Buf2;
                        if(ident == TID_PARTITION_DESC) {
                            RC = UDFVerifyPartDesc(Vcb,Buf2);
                            if(!OS_SUCCESS(RC)) try_return(RC);
                        } else if(ident == TID_UNALLOC_SPACE_DESC) {
                            RC = UDFVerifyFreeSpaceBitmap(Vcb,0,NULL,j);
                            Vcb->Modified = FALSE;
                            if(!OS_SUCCESS(RC))
                                try_return(RC);
                        }
                    }
                    MyFreePool__(Buf2);
                    Buf2 = NULL;
                }
            }
        }
try_exit: NOTHING;

    } _SEH2_FINALLY {
        if(Buf) MyFreePool__(Buf);
        if(Buf2) MyFreePool__(Buf2);
    } _SEH2_END;

    return RC;
} // end UDFVerifySequence()

/*
    remember some useful info about FileSet & RootDir location
 */
void
UDFLoadFileset(
     IN PVCB            Vcb,
     IN PFILE_SET_DESC  fset,
    OUT lb_addr         *root,
    OUT lb_addr         *sysstream
    )
{
    *root = fset->rootDirectoryICB.extLocation;
    Vcb->SerialNumber = fset->descTag.tagSerialNum;
    UDFPrint(("Rootdir at block=%x, partition=%d\n",
        root->logicalBlockNum, root->partitionReferenceNum));
    if(sysstream) {
        *sysstream = fset->streamDirectoryICB.extLocation;
        UDFPrint(("SysStream at block=%x, partition=%d\n",
            sysstream->logicalBlockNum, sysstream->partitionReferenceNum));
    }
#define CUR_IDENT_SZ (sizeof(fset->logicalVolIdent))
    if (Vcb->VolIdent.Buffer) {
        MyFreePool__(Vcb->VolIdent.Buffer);
    }
    UDFGetDstring(&(Vcb->VolIdent), (dstring*)&(fset->logicalVolIdent), CUR_IDENT_SZ);
#undef CUR_IDENT_SZ
    UDFPrint(("volIdent[] = '%ws'\n", Vcb->VolIdent.Buffer));
    // Get current UDF revision
    // Get Read-Only flags
    UDFReadEntityID_Domain(Vcb, &(fset->domainIdent));

} // end UDFLoadFileset()

OSSTATUS
UDFIsCachedBadSequence(
    IN PVCB Vcb,
    IN uint32 Lba
    )
{
    ULONG j;
    OSSTATUS RC = STATUS_SUCCESS;
    // Check if it is known bad sequence
    for(j=0; j<Vcb->BadSeqLocIndex; j++) {
        if(Vcb->BadSeqLoc[j] == Lba) {
            RC = Vcb->BadSeqStatus[j];
            break;
        }
    }
    return RC;
} // end UDFIsCachedBadSequence()

VOID
UDFRememberBadSequence(
    IN PVCB Vcb,
    IN uint32 Lba,
    IN OSSTATUS RC
    )
{
    int j;
    if(!OS_SUCCESS(UDFIsCachedBadSequence(Vcb, Lba)))
        return;
    // Remenber bad sequence
    j = Vcb->BadSeqLocIndex;
    Vcb->BadSeqLocIndex++;
    Vcb->BadSeqLoc[j]    = Lba;
    Vcb->BadSeqStatus[j] = RC;
} // end UDFRememberBadSequence()

/*
    load partition info
 */
OSSTATUS
UDFLoadPartition(
     IN PDEVICE_OBJECT  DeviceObject,
     IN PVCB            Vcb,
    OUT lb_addr         *fileset
    )
{
    OSSTATUS            RC = STATUS_UNRECOGNIZED_VOLUME;
    OSSTATUS            RC2 = STATUS_UNRECOGNIZED_VOLUME;
    AnchorVolDescPtr    *anchor;
    uint16              ident;
    int8*               Buf = (int8*)MyAllocatePool__(NonPagedPool,Vcb->BlockSize);
    uint32              main_s, main_e;
    uint32              reserve_s, reserve_e;
    int                 i;

    if(!Buf) return STATUS_INSUFFICIENT_RESOURCES;
    // walk through all available Anchors & load data
    for (i=0; i<MAX_ANCHOR_LOCATIONS; i++)
    {
        if(Vcb->Anchor[i] && (OS_SUCCESS(UDFReadTagged(Vcb, Buf,
            Vcb->Anchor[i], Vcb->Anchor[i] - Vcb->FirstLBA, &ident))))
        {
            anchor = (AnchorVolDescPtr *)Buf;

            // Locate the main sequence
            main_s = ( anchor->mainVolDescSeqExt.extLocation );
            main_e = ( anchor->mainVolDescSeqExt.extLength );
            main_e = main_e >> Vcb->BlockSizeBits;
            main_e += main_s;

            // Locate the reserve sequence
            reserve_s = (anchor->reserveVolDescSeqExt.extLocation);
            reserve_e = (anchor->reserveVolDescSeqExt.extLength);
            reserve_e = reserve_e >> Vcb->BlockSizeBits;
            reserve_e += reserve_s;

            // Check if it is known bad sequence
            RC = UDFIsCachedBadSequence(Vcb, main_s);
            if(OS_SUCCESS(RC)) {
                // Process the main & reserve sequences
                // responsible for finding the PartitionDesc(s)
                UDFPrint(("-----------------------------------\n"));
                UDFPrint(("UDF: Main sequence:\n"));
                RC = UDFProcessSequence(DeviceObject, Vcb, main_s, main_e, fileset);
            }

            if(!OS_SUCCESS(RC)) {
                // Remenber bad sequence
                UDFRememberBadSequence(Vcb, main_s, RC);

                UDFPrint(("-----------------------------------\n"));
                UDFPrint(("UDF: Main sequence failed.\n"));
                UDFPrint(("UDF: Reserve sequence\n"));
                if(Vcb->LVid) MyFreePool__(Vcb->LVid);
                Vcb->LVid = NULL;

                RC2 = UDFIsCachedBadSequence(Vcb, reserve_s);
                if(OS_SUCCESS(RC2)) {
                    RC2 = UDFProcessSequence(DeviceObject, Vcb, reserve_s, reserve_e, fileset);
                }

                if(OS_SUCCESS(RC2)) {
                    UDFPrint(("-----------------------------------\n"));
                    Vcb->VDS2_Len = reserve_e - reserve_s;
                    Vcb->VDS2 = reserve_s;
                    RC = STATUS_SUCCESS;
                    // Vcb is already Zero-filled
//                    Vcb->VDS1_Len = 0;
//                    Vcb->VDS1 = 0;
                    break;
                } else {
                    // This is also bad sequence. Remenber it too
                    UDFRememberBadSequence(Vcb, reserve_s, RC);
                }
            } else {
                // remember these values for umount__
                Vcb->VDS1_Len = main_e - main_s;
                Vcb->VDS1 = main_s;
/*                if(Vcb->LVid) MyFreePool__(Vcb->LVid);
                Vcb->LVid = NULL;*/
                if(OS_SUCCESS(UDFVerifySequence(DeviceObject, Vcb, reserve_s, reserve_e, fileset)))
                {
                    UDFPrint(("-----------------------------------\n"));
                    Vcb->VDS2_Len = reserve_e - reserve_s;
                    Vcb->VDS2 = reserve_s;
                    break;
                } else {
                    UDFPrint(("UDF: Reserve sequence verification failed.\n"));
                    switch(Vcb->PartitialDamagedVolumeAction) {
                    case UDF_PART_DAMAGED_RO:
                        UDFPrint(("UDF: Switch to r/o mode.\n"));
                        Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
                        break;
                    case UDF_PART_DAMAGED_NO:
                        UDFPrint(("UDF: Switch to raw mount mode, return UNRECOGNIZED_VOLUME.\n"));
                        Vcb->VCBFlags |= UDF_VCB_FLAGS_RAW_DISK;
                        RC = STATUS_WRONG_VOLUME;
                        break;
                    case UDF_PART_DAMAGED_RW:
                    default:
                        UDFPrint(("UDF: Keep r/w mode for your own risk.\n"));
                        break;
                    }
                }
                break;
            }
        }
    }

    if(Vcb->SparingCount &&
       (Vcb->NoFreeRelocationSpaceVolumeAction != UDF_PART_DAMAGED_RW)) {
        UDFPrint(("UDF: No free Sparing Entries -> Switch to r/o mode.\n"));
        Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
    }

    if(i == sizeof(Vcb->Anchor)/sizeof(int)) {
        UDFPrint(("No Anchor block found\n"));
        RC = STATUS_UNRECOGNIZED_VOLUME;
#ifdef UDF_DBG
    } else {
        UDFPrint(("Using anchor in block %x\n", Vcb->Anchor[i]));
#endif // UDF_DBG
    }
    MyFreePool__(Buf);
    return RC;
} // end UDFLoadPartition()

/*
    This routine scans FileSet sequence & returns pointer to last valid
    FileSet
 */
OSSTATUS
UDFFindLastFileSet(
    IN PVCB Vcb,
    IN lb_addr *Addr,  // Addr for the 1st FileSet
    IN OUT PFILE_SET_DESC FileSetDesc
    )
{
    OSSTATUS status;
    uint32 relLocExt = Addr->logicalBlockNum;
    uint32 locExt = UDFPartLbaToPhys(Vcb, Addr);
    uint16 Ident;
    uint32 relPrevExt, prevExt;

    relPrevExt, prevExt = NULL;
    FileSetDesc->nextExt.extLength = 1;  // ;)
    // walk through FileSet chain
    // we've just pre-init'd extent length to read 1st FileSet
    while(FileSetDesc->nextExt.extLength) {
        status = UDFReadTagged(Vcb, (int8*)FileSetDesc, locExt, relLocExt, &Ident);
        if(!OS_SUCCESS(status)) {
            FileSetDesc->nextExt.extLength = 0;
            return status;
        }
        UDFRegisterFsStructure(Vcb, locExt, Vcb->BlockSize);
        if((locExt == LBA_OUT_OF_EXTENT) || (Ident != TID_FILE_SET_DESC)) {
            // try to read previous FileSet
            if(!prevExt) return STATUS_UNRECOGNIZED_VOLUME;
            status = UDFReadTagged(Vcb, (int8*)FileSetDesc, prevExt, relLocExt, &Ident);
            if(OS_SUCCESS(status)) {
                UDFRegisterFsStructure(Vcb, prevExt, Vcb->BlockSize);
            }
            return status;
        }
        prevExt = locExt;
        relPrevExt = relLocExt;
        locExt = UDFPartLbaToPhys(Vcb, &(FileSetDesc->nextExt.extLocation));
    }
    return STATUS_SUCCESS;
} // end UDFFindLastFileSet()

/*
    This routine reads all sparing tables & stores them in contiguos memory
    space
 */
OSSTATUS
UDFLoadSparingTable(
    IN PVCB Vcb,
    IN PSPARABLE_PARTITION_MAP PartMap
    )
{
    PSPARING_MAP RelocMap;
    PSPARING_MAP NewRelocMap;
    OSSTATUS status;
    uint32 i=0, BC, BC2;
    PSPARING_TABLE SparTable;
    uint32 TabSize, NewSize;
    SIZE_T ReadBytes;
    uint32 SparTableLoc;
#ifdef UDF_TRACK_FS_STRUCTURES
    uint32 j;
#endif //UDF_TRACK_FS_STRUCTURES
    uint32 n,m;
    BOOLEAN merged;

    Vcb->SparingCountFree = -1;

    UDFPrint(("UDF: Sparable Part Map:\n"));
    Vcb->SparingTableLength = PartMap->sizeSparingTable;
    BC = (PartMap->sizeSparingTable >> Vcb->BlockSizeBits) + 1;
    UDFPrint((" partitionMapType   = %x\n", PartMap->partitionMapType));
    UDFPrint((" partitionMapLength = %x\n", PartMap->partitionMapLength));
    UDFPrint((" volSeqNum          = %x\n", PartMap->volSeqNum));
    UDFPrint((" partitionNum       = %x\n", PartMap->partitionNum));
    UDFPrint((" packetLength       = %x\n", PartMap->packetLength));
    UDFPrint((" numSparingTables   = %x\n", PartMap->numSparingTables));
    UDFPrint((" sizeSparingTable   = %x\n", PartMap->sizeSparingTable));
    SparTable = (PSPARING_TABLE)MyAllocatePool__(NonPagedPool, BC*Vcb->BlockSize);
    if(!SparTable) return STATUS_INSUFFICIENT_RESOURCES;
    if(Vcb->SparingTable) {
        // if a part of Sparing Table is already loaded,
        // update it with data from another one
        RelocMap = Vcb->SparingTable;
        TabSize = Vcb->SparingCount * sizeof(SPARING_ENTRY);
    } else {
        // do some init to load first part of Sparing Table
        RelocMap = (PSPARING_MAP)MyAllocatePool__(NonPagedPool, RELOC_MAP_GRAN);
        if(!RelocMap) {
            MyFreePool__(SparTable);
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        TabSize = RELOC_MAP_GRAN;
        Vcb->SparingBlockSize = PartMap->packetLength;
    }
    // walk through all available Sparing Tables
    for(i=0;i<PartMap->numSparingTables;i++) {
        // read (next) table
        SparTableLoc = ((uint32*)(PartMap+1))[i];
        for(n=0; n<Vcb->SparingTableCount; n++) {
            if(Vcb->SparingTableLoc[i] == SparTableLoc) {
                UDFPrint((" already processed @%x\n",
                    SparTableLoc
                    ));
                continue;
            }
        }
        status = UDFReadSectors(Vcb, FALSE, SparTableLoc, 1, FALSE, (int8*)SparTable, &ReadBytes);
        // tag should be set to TID_UNUSED_DESC
        if(OS_SUCCESS(status) && (SparTable->descTag.tagIdent == TID_UNUSED_DESC)) {

            UDFRegisterFsStructure(Vcb,  SparTableLoc, Vcb->BlockSize);
            BC2 = ((sizeof(SPARING_TABLE) +
                    SparTable->reallocationTableLen*sizeof(SparingEntry) +
                    Vcb->BlockSize-1)
                                      >> Vcb->BlockSizeBits);
            if(BC2 > BC) {
                UDFPrint((" sizeSparingTable @%x too long: %x > %x\n",
                    SparTableLoc, BC2, BC
                    ));
                continue;
            }
            status = UDFReadSectors(Vcb, FALSE, SparTableLoc,
                BC2, FALSE, (int8*)SparTable, &ReadBytes);
            UDFRegisterFsStructure(Vcb,  SparTableLoc, BC2<<Vcb->BlockSizeBits);

            if(!OS_SUCCESS(status)) {
                UDFPrint((" Error reading sizeSparingTable @%x (%x)\n",
                    SparTableLoc, BC2
                    ));
                continue;
            }
            // process sparing table
            NewSize = sizeof(SparingEntry)*SparTable->reallocationTableLen;
            TabSize = MyReallocPool__((int8*)RelocMap, TabSize, (int8**)&RelocMap, TabSize+NewSize);
            if(!TabSize) {
                MyFreePool__(SparTable);
                return STATUS_INSUFFICIENT_RESOURCES;
            }

#ifdef UDF_TRACK_FS_STRUCTURES
            for(j=0; j<SparTable->reallocationTableLen; j++) {
                UDFRegisterFsStructure(Vcb,  ((SparingEntry*)(SparTable+1))[j].mappedLocation, Vcb->WriteBlockSize);
            }
#endif //UDF_TRACK_FS_STRUCTURES

            Vcb->SparingTableLoc[Vcb->SparingTableCount] = SparTableLoc;
            Vcb->SparingTableCount++;

            NewRelocMap = (PSPARING_MAP)(SparTable+1);
            for(n=0; n<SparTable->reallocationTableLen; n++) {
                merged = TRUE;
                for(m=0; m<Vcb->SparingCount; m++) {
                    if(RelocMap[m].mappedLocation == NewRelocMap[n].mappedLocation) {
                        UDFPrint(("  dup @%x (%x) vs @%x (%x)\n",
                            RelocMap[m].origLocation, RelocMap[m].mappedLocation,
                            NewRelocMap[m].origLocation, NewRelocMap[m].mappedLocation));
                        merged = FALSE;
                    }
                    if((RelocMap[m].origLocation   == NewRelocMap[n].origLocation) &&
                       (RelocMap[m].mappedLocation != NewRelocMap[n].mappedLocation) &&
                       (RelocMap[m].origLocation != SPARING_LOC_AVAILABLE) &&
                       (RelocMap[m].origLocation != SPARING_LOC_CORRUPTED)) {
                        UDFPrint(("  conflict @%x (%x) vs @%x (%x)\n",
                            RelocMap[m].origLocation, RelocMap[m].mappedLocation,
                            NewRelocMap[n].origLocation, NewRelocMap[n].mappedLocation));
                        merged = FALSE;
                    }
                }
                if(merged) {
                    RelocMap[Vcb->SparingCount] = NewRelocMap[n];
                    UDFPrint(("  reloc %x -> %x\n",
                        RelocMap[Vcb->SparingCount].origLocation, RelocMap[Vcb->SparingCount].mappedLocation));
                    Vcb->SparingCount++;
                    if(RelocMap[Vcb->SparingCount].origLocation == SPARING_LOC_AVAILABLE) {
                        Vcb->NoFreeRelocationSpaceVolumeAction = UDF_PART_DAMAGED_RW;
                    }
                }
            }

/*
            RtlCopyMemory((int8*)(RelocMap+Vcb->SparingCount),
                          (int8*)(SparTable+1), NewSize);
            Vcb->SparingCount += NewSize/sizeof(SPARING_ENTRY);
*/
            if(Vcb->SparingTableCount >= MAX_SPARING_TABLE_LOCATIONS) {
                UDFPrint(("    too many Sparing Tables\n"));
                break;
            }
        }
    }
    Vcb->SparingTable = RelocMap;
    MyFreePool__(SparTable);
    return STATUS_SUCCESS;
} // end UDFLoadSparingTable()

/*
    This routine checks if buffer is ZERO-filled
 */
BOOLEAN
UDFCheckZeroBuf(
    IN int8* Buf,
    IN uint32 Length
    )
{

#if defined(_X86_) && defined(_MSC_VER) && !defined(__clang__)

    BOOLEAN RC = FALSE;

    uint32 len = Length;
    __asm push  ecx
    __asm push  edi

    __asm mov   ecx,len
    __asm mov   edi,Buf
    __asm xor   eax,eax
    __asm shr   ecx,2
    __asm repe scasd
    __asm jne   short not_all_zeros
    __asm mov   RC,1

not_all_zeros:

    __asm pop   edi
    __asm pop   ecx

    return RC;

#else // _X86_

    uint32* tmp = (uint32*)Buf;
    uint32 i;

    for(i=0; i<Length/4; i++) {
        if(tmp[i]) return FALSE;
    }
    return TRUE;

#endif // _X86_

} // end UDFCheckZeroBuf()

/*
    check if this is an UDF-formatted disk
*/
OSSTATUS
UDFGetDiskInfoAndVerify(
    IN PDEVICE_OBJECT DeviceObject,      // the target device object
    IN PVCB           Vcb                // Volume control block from this DevObj
    )
{
    OSSTATUS        RC = STATUS_UNRECOGNIZED_VOLUME;
    uint32          NSRDesc;
    lb_addr         fileset;
    PFILE_SET_DESC  FileSetDesc = NULL;

    int8*           Buf = NULL;
    SIZE_T          ReadBytes;

    UDFPrint(("UDFGetDiskInfoAndVerify\n"));
    _SEH2_TRY {

        if(!UDFFindAnchor(Vcb)) {
            if(Vcb->FsDeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) {
                // check if this disc is mountable for CDFS
                UDFPrint(("   FILE_DEVICE_CD_ROM_FILE_SYSTEM\n"));
check_NSR:
                NSRDesc = UDFFindVRS(Vcb);
                if(!(NSRDesc & VRS_ISO9660_FOUND)) {
                    // no CDFS VRS found
                    UDFPrint(("UDFGetDiskInfoAndVerify: no CDFS VRS found\n"));
                    if(!Vcb->TrackMap[Vcb->LastTrackNum].LastLba &&
                       !Vcb->TrackMap[Vcb->FirstTrackNum].LastLba) {
                        // such a stupid method of Audio-CD detection...
                        UDFPrint(("UDFGetDiskInfoAndVerify: set UDF_VCB_FLAGS_RAW_DISK\n"));
                        Vcb->VCBFlags |= UDF_VCB_FLAGS_RAW_DISK;
                    }
                }
                Vcb->NSRDesc = NSRDesc;

                Buf = (int8*)MyAllocatePool__(NonPagedPool, 0x10000);
                if(!Buf) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
                RC = UDFReadData(Vcb, FALSE, 0, 0x10000, FALSE, Buf, &ReadBytes);
                if(!OS_SUCCESS(RC))
                    try_return(RC = STATUS_UNRECOGNIZED_VOLUME);
                RC = STATUS_UNRECOGNIZED_VOLUME;
                if(!UDFCheckZeroBuf(Buf,0x10000)) {
                    UDFPrint(("UDFGetDiskInfoAndVerify: possible FS detected, remove UDF_VCB_FLAGS_RAW_DISK\n"));
                    Vcb->VCBFlags &= ~UDF_VCB_FLAGS_RAW_DISK;
                }
                MyFreePool__(Buf);
                Buf = NULL;
            }
            try_return(RC = STATUS_UNRECOGNIZED_VOLUME);
        }

        RC = UDFLoadPartition(DeviceObject,Vcb,&fileset);
        if(!OS_SUCCESS(RC)) {
            if(RC == STATUS_UNRECOGNIZED_VOLUME) {
                UDFPrint(("UDFGetDiskInfoAndVerify: check NSR presence\n"));
                goto check_NSR;
            }
            try_return(RC);
        }

        FileSetDesc = (PFILE_SET_DESC)MyAllocatePool__(NonPagedPool,Vcb->BlockSize);
        if(!FileSetDesc) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);

        RC = UDFFindLastFileSet(Vcb,&fileset,FileSetDesc);
        if(!OS_SUCCESS(RC)) try_return(RC);

        UDFLoadFileset(Vcb,FileSetDesc, &(Vcb->RootLbAddr), &(Vcb->SysStreamLbAddr));

        Vcb->FSBM_OldBitmap = (int8*)DbgAllocatePool(NonPagedPool, Vcb->FSBM_ByteCount);
        if(!(Vcb->FSBM_OldBitmap)) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
        RtlCopyMemory(Vcb->FSBM_OldBitmap, Vcb->FSBM_Bitmap, Vcb->FSBM_ByteCount);

try_exit:   NOTHING;
    } _SEH2_FINALLY {
        if(FileSetDesc)   MyFreePool__(FileSetDesc);
        if(Buf)           MyFreePool__(Buf);
    } _SEH2_END;

    return(RC);

} // end UDFGetDiskInfoAndVerify()