VirtualBox

Changeset 39577 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Dec 10, 2011 8:35:11 PM (13 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
75337
Message:

Storage/VHD: Implement repair callback

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Storage/VHD.cpp

    r39519 r39577  
    30363036    }
    30373037    /* Same size doesn't change the image at all. */
     3038
     3039    LogFlowFunc(("returns %Rrc\n", rc));
     3040    return rc;
     3041}
     3042
     3043/** @copydoc VBOXHDDBACKEND::pfnRepair */
     3044static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
     3045                                   PVDINTERFACE pVDIfsImage, uint32_t fFlags)
     3046{
     3047    LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
     3048    int rc;
     3049    PVDINTERFACEERROR pIfError;
     3050    PVDINTERFACEIOINT pIfIo;
     3051    PVDIOSTORAGE pStorage;
     3052    uint64_t cbFile;
     3053    VHDFooter vhdFooter;
     3054    VHDDynamicDiskHeader dynamicDiskHeader;
     3055    uint32_t *paBat = NULL;
     3056    uint32_t *pu32BlockBitmap = NULL;
     3057
     3058    pIfIo = VDIfIoIntGet(pVDIfsImage);
     3059    AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
     3060
     3061    pIfError = VDIfErrorGet(pVDIfsDisk);
     3062
     3063    do
     3064    {
     3065        uint64_t offDynamicDiskHeader = 0;
     3066        uint64_t offBat = 0;
     3067        uint64_t offFooter = 0;
     3068        uint32_t cBatEntries = 0;
     3069        bool fDynamic = false;
     3070        bool fRepairFooter = false;
     3071        bool fRepairBat = false;
     3072        bool fRepairDynHeader = false;
     3073
     3074        rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
     3075                               VDOpenFlagsToFileOpenFlags(  fFlags & VD_REPAIR_DRY_RUN
     3076                                                          ? VD_OPEN_FLAGS_READONLY
     3077                                                          : 0,
     3078                                                          false /* fCreate */),
     3079                               &pStorage);
     3080        if (RT_FAILURE(rc))
     3081        {
     3082            rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to open image \"%s\"", pszFilename);
     3083            break;
     3084        }
     3085
     3086        rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
     3087        if (RT_FAILURE(rc))
     3088        {
     3089            rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to query image size");
     3090            break;
     3091        }
     3092
     3093        if (cbFile < sizeof(VHDFooter))
     3094        {
     3095            rc = vdIfError(pIfError, VERR_VD_INVALID_SIZE, RT_SRC_POS,
     3096                           "Image must be at least %u bytes (got %llu)",
     3097                           sizeof(VHDFooter), cbFile);
     3098            break;
     3099        }
     3100
     3101        rc = vdIfIoIntFileReadSync(pIfIo, pStorage, cbFile - sizeof(VHDFooter),
     3102                                   &vhdFooter, sizeof(VHDFooter), NULL);
     3103        if (RT_FAILURE(rc))
     3104        {
     3105            rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read footer of image");
     3106            break;
     3107        }
     3108
     3109        if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
     3110        {
     3111            /* Dynamic images have a backup at the beginning of the image. */
     3112            rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0,
     3113                                       &vhdFooter, sizeof(VHDFooter), NULL);
     3114            if (RT_FAILURE(rc))
     3115            {
     3116                rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read header of image");
     3117                break;
     3118            }
     3119
     3120            /*
     3121             * Check for the header, if this fails the image is either completely corrupted
     3122             * and impossible to repair or in another format.
     3123             */
     3124            if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
     3125            {
     3126                rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3127                               "No valid VHD structures found");
     3128                break;
     3129            }
     3130            else
     3131                vdIfErrorMessage(pIfError, "Missing footer structure, using backup");
     3132
     3133            /* Remember to fix the footer structure. */
     3134            fRepairFooter = true;
     3135        }
     3136
     3137        offFooter = cbFile - sizeof(VHDFooter);
     3138
     3139        /* Verify that checksums match. */
     3140        uint32_t u32ChkSumOld = RT_BE2H_U32(vhdFooter.Checksum);
     3141        vhdFooter.Checksum = 0;
     3142        uint32_t u32ChkSum = vhdChecksum(&vhdFooter, sizeof(VHDFooter));
     3143
     3144        vhdFooter.Checksum = RT_H2BE_U32(u32ChkSum);
     3145
     3146        if (u32ChkSumOld != u32ChkSum)
     3147        {
     3148            vdIfErrorMessage(pIfError, "Checksum is invalid (should be %u got %u), repairing",
     3149                             u32ChkSum, u32ChkSumOld);
     3150            fRepairFooter = true;
     3151            break;
     3152        }
     3153
     3154        switch (RT_BE2H_U32(vhdFooter.DiskType))
     3155        {
     3156            case VHD_FOOTER_DISK_TYPE_FIXED:
     3157                fDynamic = false;
     3158                break;
     3159            case VHD_FOOTER_DISK_TYPE_DYNAMIC:
     3160                fDynamic = true;
     3161                break;
     3162            case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
     3163                fDynamic = true;
     3164                break;
     3165            default:
     3166            {
     3167                rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3168                               "VHD image type %u is not supported",
     3169                               RT_BE2H_U32(vhdFooter.DiskType));
     3170                break;
     3171            }
     3172        }
     3173
     3174        /* Load and check dynamic disk header if required. */
     3175        if (fDynamic)
     3176        {
     3177            size_t cbBlock;
     3178
     3179            offDynamicDiskHeader = RT_BE2H_U64(vhdFooter.DataOffset);
     3180            if (offDynamicDiskHeader + sizeof(VHDDynamicDiskHeader) > cbFile)
     3181            {
     3182                rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3183                               "VHD image type is not supported");
     3184                break;
     3185            }
     3186
     3187            rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offDynamicDiskHeader,
     3188                                       &dynamicDiskHeader, sizeof(VHDDynamicDiskHeader), NULL);
     3189            if (RT_FAILURE(rc))
     3190            {
     3191                rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3192                               "Failed to read dynamic disk header (at %llu), %Rrc",
     3193                               offDynamicDiskHeader, rc);
     3194                break;
     3195            }
     3196
     3197            /* Verify that checksums match. */
     3198            u32ChkSumOld = RT_BE2H_U32(dynamicDiskHeader.Checksum);
     3199            dynamicDiskHeader.Checksum = 0;
     3200            u32ChkSum = vhdChecksum(&dynamicDiskHeader, sizeof(VHDDynamicDiskHeader));
     3201
     3202            dynamicDiskHeader.Checksum = RT_H2BE_U32(u32ChkSum);
     3203
     3204            if (u32ChkSumOld != u32ChkSum)
     3205            {
     3206                vdIfErrorMessage(pIfError, "Checksum of dynamic disk header is invalid (should be %u got %u), repairing",
     3207                                 u32ChkSum, u32ChkSumOld);
     3208                fRepairDynHeader = true;
     3209                break;
     3210            }
     3211
     3212            /* Read the block allocation table and fix any inconsistencies. */
     3213            offBat = RT_BE2H_U64(dynamicDiskHeader.TableOffset);
     3214            cBatEntries = RT_BE2H_U32(dynamicDiskHeader.MaxTableEntries);
     3215            cbBlock = RT_BE2H_U32(dynamicDiskHeader.BlockSize);
     3216            cbBlock += cbBlock / VHD_SECTOR_SIZE / 8;
     3217
     3218            if (offBat + cBatEntries * sizeof(uint32_t) > cbFile)
     3219            {
     3220                rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3221                               "Block allocation table is not inside the image");
     3222                break;
     3223            }
     3224
     3225            paBat = (uint32_t *)RTMemAllocZ(cBatEntries * sizeof(uint32_t));
     3226            if (!paBat)
     3227            {
     3228                rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3229                               "Could not allocate memory for the block allocation table (%u bytes)",
     3230                               cBatEntries * sizeof(uint32_t));
     3231                break;
     3232            }
     3233
     3234            rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offBat, paBat,
     3235                                       cBatEntries * sizeof(uint32_t), NULL);
     3236            if (RT_FAILURE(rc))
     3237            {
     3238                rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3239                               "Could not read block allocation table (at %llu), %Rrc",
     3240                               offBat, rc);
     3241                break;
     3242            }
     3243
     3244            pu32BlockBitmap = (uint32_t *)RTMemAllocZ(RT_ALIGN_Z(cBatEntries, 4));
     3245            if (!pu32BlockBitmap)
     3246            {
     3247                rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
     3248                               "Failed to allocate memory for block bitmap");
     3249                break;
     3250            }
     3251
     3252            uint32_t idxMinBlock = UINT32_C(0xffffffff);
     3253            for (uint32_t i = 0; i < cBatEntries; i++)
     3254            {
     3255                paBat[i] = RT_BE2H_U32(paBat[i]);
     3256                if (paBat[i] < idxMinBlock)
     3257                    idxMinBlock = paBat[i];
     3258            }
     3259
     3260            vdIfErrorMessage(pIfError, "First data block at sector %u", idxMinBlock);
     3261
     3262            for (uint32_t i = 0; i < cBatEntries; i++)
     3263            {
     3264                if (paBat[i] != UINT32_C(0xffffffff))
     3265                {
     3266                    uint64_t offBlock =(uint64_t)paBat[i] * VHD_SECTOR_SIZE;
     3267
     3268                    /*
     3269                     * Check that the offsets are valid (inside of the image) and
     3270                     * that there are no double references.
     3271                     */
     3272                    if (offBlock + cbBlock > cbFile)
     3273                    {
     3274                        vdIfErrorMessage(pIfError, "Entry %u points to invalid offset %llu, clearing",
     3275                                         i, offBlock);
     3276                        paBat[i] = 0;
     3277                        fRepairBat = true;
     3278                    }
     3279                    else if (offBlock + cbBlock > offFooter)
     3280                    {
     3281                        vdIfErrorMessage(pIfError, "Entry %u intersects with footer, aligning footer",
     3282                                         i);
     3283                        offFooter = offBlock + cbBlock;
     3284                        fRepairBat = true;
     3285                    }
     3286
     3287                    if (ASMBitTestAndSet(pu32BlockBitmap, (paBat[i] - idxMinBlock) / (cbBlock / VHD_SECTOR_SIZE)))
     3288                    {
     3289                        vdIfErrorMessage(pIfError, "Entry %u points to an already referenced data block, clearing",
     3290                                         i);
     3291                        paBat[i] = 0;
     3292                        fRepairBat = true;
     3293                    }
     3294                }
     3295            }
     3296        }
     3297
     3298        /* Write repaired structures now. */
     3299        if (!(fRepairBat || fRepairDynHeader || fRepairFooter))
     3300            vdIfErrorMessage(pIfError, "VHD image is in a consistent state, no repair required");
     3301        else if (!(fFlags & VD_REPAIR_DRY_RUN))
     3302        {
     3303            if (fRepairBat)
     3304            {
     3305                for (uint32_t i = 0; i < cBatEntries; i++)
     3306                    paBat[i] = RT_H2BE_U32(paBat[i]);
     3307
     3308                vdIfErrorMessage(pIfError, "Writing repaired block allocation table...");
     3309
     3310                rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offBat, paBat,
     3311                                            cBatEntries * sizeof(uint32_t), NULL);
     3312                if (RT_FAILURE(rc))
     3313                {
     3314                    rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3315                                   "Could not write repaired block allocation table (at %llu), %Rrc",
     3316                                   offBat, rc);
     3317                    break;
     3318                }
     3319            }
     3320
     3321            if (fRepairDynHeader)
     3322            {
     3323                Assert(fDynamic);
     3324
     3325                vdIfErrorMessage(pIfError, "Writing repaired dynamic disk header...");
     3326                rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offDynamicDiskHeader, &dynamicDiskHeader,
     3327                                            sizeof(VHDDynamicDiskHeader), NULL);
     3328                if (RT_FAILURE(rc))
     3329                {
     3330                    rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3331                                   "Could not write repaired dynamic disk header (at %llu), %Rrc",
     3332                                   offDynamicDiskHeader, rc);
     3333                    break;
     3334                }
     3335            }
     3336
     3337            if (fRepairFooter)
     3338            {
     3339                vdIfErrorMessage(pIfError, "Writing repaired Footer...");
     3340
     3341                if (fDynamic)
     3342                {
     3343                    /* Write backup at image beginning. */
     3344                    rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, 0, &vhdFooter,
     3345                                                sizeof(VHDFooter), NULL);
     3346                    if (RT_FAILURE(rc))
     3347                    {
     3348                        rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3349                                       "Could not write repaired backup footer (at %llu), %Rrc",
     3350                                       0, rc);
     3351                        break;
     3352                    }
     3353                }
     3354
     3355                rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offFooter, &vhdFooter,
     3356                                            sizeof(VHDFooter), NULL);
     3357                if (RT_FAILURE(rc))
     3358                {
     3359                    rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
     3360                                   "Could not write repaired footer (at %llu), %Rrc",
     3361                                   cbFile - sizeof(VHDFooter), rc);
     3362                    break;
     3363                }
     3364            }
     3365
     3366            vdIfErrorMessage(pIfError, "Corrupted VHD image repaired successfully");
     3367        }
     3368    } while(0);
     3369
     3370    if (paBat)
     3371        RTMemFree(paBat);
     3372
     3373    if (pu32BlockBitmap)
     3374        RTMemFree(pu32BlockBitmap);
     3375
     3376    if (pStorage)
     3377        vdIfIoIntFileClose(pIfIo, pStorage);
    30383378
    30393379    LogFlowFunc(("returns %Rrc\n", rc));
     
    31453485    NULL,
    31463486    /* pfnRepair */
    3147     NULL
     3487    vhdRepair
    31483488};
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette