~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/VBox/Devices/Storage/VBoxHDD-new.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** $Id$ */
 
2
/** @file
 
3
 * VBox HDD Container implementation.
 
4
 */
 
5
 
 
6
/*
 
7
 * Copyright (C) 2006-2007 innotek GmbH
 
8
 *
 
9
 * This file is part of VirtualBox Open Source Edition (OSE), as
 
10
 * available from http://www.virtualbox.org. This file is free software;
 
11
 * you can redistribute it and/or modify it under the terms of the GNU
 
12
 * General Public License as published by the Free Software Foundation,
 
13
 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
 
14
 * distribution. VirtualBox OSE is distributed in the hope that it will
 
15
 * be useful, but WITHOUT ANY WARRANTY of any kind.
 
16
 */
 
17
 
 
18
/*******************************************************************************
 
19
*   Header Files                                                               *
 
20
*******************************************************************************/
 
21
#define LOG_GROUP LOG_GROUP_VD
 
22
#include <VBox/VBoxHDD-new.h>
 
23
#include <VBox/err.h>
 
24
 
 
25
#include <VBox/log.h>
 
26
#include <iprt/alloc.h>
 
27
#include <iprt/assert.h>
 
28
#include <iprt/uuid.h>
 
29
#include <iprt/file.h>
 
30
#include <iprt/string.h>
 
31
#include <iprt/asm.h>
 
32
 
 
33
#include "VBoxHDD-newInternal.h"
 
34
 
 
35
 
 
36
#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
 
37
 
 
38
 
 
39
/**
 
40
 * VBox HDD Container image descriptor.
 
41
 */
 
42
typedef struct VDIMAGE
 
43
{
 
44
    /** Link to parent image descriptor, if any. */
 
45
    struct VDIMAGE  *pPrev;
 
46
    /** Link to child image descriptor, if any. */
 
47
    struct VDIMAGE  *pNext;
 
48
    /** Container base filename. (UTF-8) */
 
49
    char            *pszFilename;
 
50
    /** Data managed by the backend which keeps the actual info. */
 
51
    void            *pvBackendData;
 
52
    /** Image open flags (only those handled generically in this code and which
 
53
     * the backends will never ever see). */
 
54
    unsigned        uOpenFlags;
 
55
} VDIMAGE, *PVDIMAGE;
 
56
 
 
57
/**
 
58
 * uModified bit flags.
 
59
 */
 
60
#define VD_IMAGE_MODIFIED_FLAG                  BIT(0)
 
61
#define VD_IMAGE_MODIFIED_FIRST                 BIT(1)
 
62
#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE   BIT(2)
 
63
 
 
64
 
 
65
/**
 
66
 * VBox HDD Container main structure, private part.
 
67
 */
 
68
struct VBOXHDD
 
69
{
 
70
    /** Structure signature (VBOXHDDDISK_SIGNATURE). */
 
71
    uint32_t            u32Signature;
 
72
 
 
73
    /** Number of opened images. */
 
74
    unsigned            cImages;
 
75
 
 
76
    /** Base image. */
 
77
    PVDIMAGE            pBase;
 
78
 
 
79
    /** Last opened image in the chain.
 
80
     * The same as pBase if only one image is used. */
 
81
    PVDIMAGE            pLast;
 
82
 
 
83
    /** Flags representing the modification state. */
 
84
    unsigned            uModified;
 
85
 
 
86
    /** Cached size of this disk. */
 
87
    uint64_t            cbSize;
 
88
    /** Cached CHS geometry for this disk, cylinders. */
 
89
    unsigned            cCylinders;
 
90
    /** Cached CHS geometry for this disk, heads. */
 
91
    unsigned            cHeads;
 
92
    /** Cached CHS geometry for this disk, sectors. */
 
93
    unsigned            cSectors;
 
94
    /** Cached translation mode for this disk. */
 
95
    PDMBIOSTRANSLATION  enmTranslation;
 
96
 
 
97
    /** Error message processing callback. */
 
98
    PFNVDERROR          pfnError;
 
99
    /** Opaque data for error callback. */
 
100
    void                *pvErrorUser;
 
101
 
 
102
    /** Function pointers for the various backend methods. */
 
103
    PVBOXHDDBACKEND     Backend;
 
104
};
 
105
 
 
106
 
 
107
typedef struct
 
108
{
 
109
    const char          *pszBackendName;
 
110
    PVBOXHDDBACKEND     Backend;
 
111
} VBOXHDDBACKENDENTRY;
 
112
 
 
113
 
 
114
extern VBOXHDDBACKEND g_VmdkBackend;
 
115
 
 
116
 
 
117
static const VBOXHDDBACKENDENTRY aBackends[] =
 
118
{
 
119
    { "VMDK", &g_VmdkBackend },
 
120
    { NULL, NULL }
 
121
};
 
122
 
 
123
 
 
124
/**
 
125
 * internal: issue early error message.
 
126
 */
 
127
static int vdEarlyError(PFNVDERROR pfnError, void *pvErrorUser, int rc,
 
128
                        RT_SRC_POS_DECL, const char *pszFormat, ...)
 
129
{
 
130
    va_list va;
 
131
    va_start(va, pszFormat);
 
132
    if (pfnError)
 
133
        pfnError(pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
 
134
    va_end(va);
 
135
    return rc;
 
136
}
 
137
 
 
138
/**
 
139
 * internal: issue error message.
 
140
 */
 
141
static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
 
142
                   const char *pszFormat, ...)
 
143
{
 
144
    va_list va;
 
145
    va_start(va, pszFormat);
 
146
    if (pDisk->pfnError)
 
147
        pDisk->pfnError(pDisk->pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
 
148
    va_end(va);
 
149
    return rc;
 
150
}
 
151
 
 
152
/**
 
153
 * internal: add image structure to the end of images list.
 
154
 */
 
155
static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
 
156
{
 
157
    pImage->pPrev = NULL;
 
158
    pImage->pNext = NULL;
 
159
 
 
160
    if (pDisk->pBase)
 
161
    {
 
162
        Assert(pDisk->cImages > 0);
 
163
        pImage->pPrev = pDisk->pLast;
 
164
        pDisk->pLast->pNext = pImage;
 
165
        pDisk->pLast = pImage;
 
166
    }
 
167
    else
 
168
    {
 
169
        Assert(pDisk->cImages == 0);
 
170
        pDisk->pBase = pImage;
 
171
        pDisk->pLast = pImage;
 
172
    }
 
173
 
 
174
    pDisk->cImages++;
 
175
}
 
176
 
 
177
/**
 
178
 * internal: remove image structure from the images list.
 
179
 */
 
180
static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
 
181
{
 
182
    Assert(pDisk->cImages > 0);
 
183
 
 
184
    if (pImage->pPrev)
 
185
        pImage->pPrev->pNext = pImage->pNext;
 
186
    else
 
187
        pDisk->pBase = pImage->pNext;
 
188
 
 
189
    if (pImage->pNext)
 
190
        pImage->pNext->pPrev = pImage->pPrev;
 
191
    else
 
192
        pDisk->pLast = pImage->pPrev;
 
193
 
 
194
    pImage->pPrev = NULL;
 
195
    pImage->pNext = NULL;
 
196
 
 
197
    pDisk->cImages--;
 
198
}
 
199
 
 
200
/**
 
201
 * internal: find image by index into the images list.
 
202
 */
 
203
static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
 
204
{
 
205
    PVDIMAGE pImage = pDisk->pBase;
 
206
    while (pImage && nImage)
 
207
    {
 
208
        pImage = pImage->pNext;
 
209
        nImage--;
 
210
    }
 
211
    return pImage;
 
212
}
 
213
 
 
214
/**
 
215
 * internal: read the specified amount of data in whatever blocks the backend
 
216
 * will give us.
 
217
 */
 
218
static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
 
219
                        void *pvBuf, size_t cbRead)
 
220
{
 
221
    int rc;
 
222
    size_t cbThisRead;
 
223
    PVDIMAGE pCurrImage;
 
224
 
 
225
    /* Loop until all read. */
 
226
    do
 
227
    {
 
228
        /* Search for image with allocated block. Do not attempt to read more
 
229
         * than the previous reads marked as valid. Otherwise this would return
 
230
         * stale data when different block sizes are used for the images. */
 
231
        cbThisRead = cbRead;
 
232
        rc = VINF_VDI_BLOCK_FREE;
 
233
        for (pCurrImage = pImage;
 
234
             pCurrImage != NULL && rc == VINF_VDI_BLOCK_FREE;
 
235
             pCurrImage = pCurrImage->pPrev)
 
236
        {
 
237
            rc = pDisk->Backend->pfnRead(pCurrImage->pvBackendData, uOffset,
 
238
                                         pvBuf, cbThisRead, &cbThisRead);
 
239
        }
 
240
 
 
241
        /* No image in the chain contains the data for the block. */
 
242
        if (rc == VINF_VDI_BLOCK_FREE)
 
243
        {
 
244
            memset(pvBuf, '\0', cbThisRead);
 
245
            rc = VINF_SUCCESS;
 
246
        }
 
247
 
 
248
        cbRead -= cbThisRead;
 
249
        uOffset += cbThisRead;
 
250
        pvBuf = (char *)pvBuf + cbThisRead;
 
251
    } while (cbRead != 0 && VBOX_SUCCESS(rc));
 
252
 
 
253
    return rc;
 
254
}
 
255
 
 
256
/**
 
257
 * internal: mark the disk as not modified.
 
258
 */
 
259
static void vdResetModifiedFlag(PVBOXHDD pDisk)
 
260
{
 
261
    if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
 
262
    {
 
263
        /* generate new last-modified uuid */
 
264
        if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
 
265
        {
 
266
            RTUUID Uuid;
 
267
 
 
268
            RTUuidCreate(&Uuid);
 
269
            pDisk->Backend->pfnSetModificationUuid(pDisk->pLast->pvBackendData,
 
270
                                                   &Uuid);
 
271
        }
 
272
 
 
273
        pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
 
274
    }
 
275
}
 
276
 
 
277
/**
 
278
 * internal: mark the disk as modified.
 
279
 */
 
280
static void vdSetModifiedFlag(PVBOXHDD pDisk)
 
281
{
 
282
    pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
 
283
    if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
 
284
    {
 
285
        pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
 
286
 
 
287
        /* First modify, so create a UUID and ensure it's written to disk. */
 
288
        vdResetModifiedFlag(pDisk);
 
289
 
 
290
        if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
 
291
            pDisk->Backend->pfnFlush(pDisk->pLast->pvBackendData);
 
292
    }
 
293
}
 
294
 
 
295
/**
 
296
 * internal: write a complete block (only used for diff images), taking the
 
297
 * remaining data from parent images. This implementation does not optimize
 
298
 * anything (except that it tries to read only that portions from parent
 
299
 * images that are really needed).
 
300
 */
 
301
static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
 
302
                                 uint64_t uOffset, size_t cbWrite,
 
303
                                 size_t cbThisWrite, size_t cbPreRead,
 
304
                                 size_t cbPostRead, const void *pvBuf,
 
305
                                 void *pvTmp)
 
306
{
 
307
    int rc = VINF_SUCCESS;
 
308
 
 
309
    /* Read the data that goes before the write to fill the block. */
 
310
    if (cbPreRead)
 
311
    {
 
312
        rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead,
 
313
                          pvTmp, cbPreRead);
 
314
        if (VBOX_FAILURE(rc))
 
315
            return rc;
 
316
    }
 
317
 
 
318
    /* Copy the data to the right place in the buffer. */
 
319
    memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
 
320
 
 
321
    /* Read the data that goes after the write to fill the block. */
 
322
    if (cbPostRead)
 
323
    {
 
324
        /* If we have data to be written, use that instead of reading
 
325
         * data from the image. */
 
326
        size_t cbWriteCopy;
 
327
        if (cbWrite > cbThisWrite)
 
328
            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
 
329
        else
 
330
            cbWriteCopy = 0;
 
331
        /* Figure out how much we cannnot read from the image, because
 
332
         * the last block to write might exceed the nominal size of the
 
333
         * image for technical reasons. */
 
334
        size_t cbFill;
 
335
        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
 
336
            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
 
337
        else
 
338
            cbFill = 0;
 
339
        /* The rest must be read from the image. */
 
340
        size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
 
341
 
 
342
        /* Now assemble the remaining data. */
 
343
        if (cbWriteCopy)
 
344
            memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
 
345
                   (char *)pvBuf + cbThisWrite, cbWriteCopy);
 
346
        if (cbReadImage)
 
347
            rc = vdReadHelper(pDisk, pImage->pPrev,
 
348
                              uOffset + cbThisWrite + cbWriteCopy,
 
349
                              (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
 
350
                              cbReadImage);
 
351
        if (VBOX_FAILURE(rc))
 
352
            return rc;
 
353
        /* Zero out the remainder of this block. Will never be visible, as this
 
354
         * is beyond the limit of the image. */
 
355
        if (cbFill)
 
356
            memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
 
357
                   '\0', cbFill);
 
358
    }
 
359
 
 
360
    /* Write the full block to the virtual disk. */
 
361
    rc = pDisk->Backend->pfnWrite(pImage->pvBackendData,
 
362
                                  uOffset - cbPreRead, pvTmp,
 
363
                                  cbPreRead + cbThisWrite + cbPostRead,
 
364
                                  NULL,
 
365
                                  &cbPreRead, &cbPostRead);
 
366
    Assert(rc != VINF_VDI_BLOCK_FREE);
 
367
    Assert(cbPreRead == 0);
 
368
    Assert(cbPostRead == 0);
 
369
 
 
370
    return rc;
 
371
}
 
372
 
 
373
/**
 
374
 * internal: write a complete block (only used for diff images), taking the
 
375
 * remaining data from parent images. This implementation optimized out writes
 
376
 * that do not change the data relative to the state as of the parent images.
 
377
 * All backends which support differential/growing images support this.
 
378
 */
 
379
static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
 
380
                                  uint64_t uOffset, size_t cbWrite,
 
381
                                  size_t cbThisWrite, size_t cbPreRead,
 
382
                                  size_t cbPostRead, const void *pvBuf,
 
383
                                  void *pvTmp)
 
384
{
 
385
    size_t cbFill = 0;
 
386
    size_t cbWriteCopy = 0;
 
387
    size_t cbReadImage = 0;
 
388
    int rc;
 
389
 
 
390
    if (cbPostRead)
 
391
    {
 
392
        /* Figure out how much we cannnot read from the image, because
 
393
         * the last block to write might exceed the nominal size of the
 
394
         * image for technical reasons. */
 
395
        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
 
396
            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
 
397
 
 
398
        /* If we have data to be written, use that instead of reading
 
399
         * data from the image. */
 
400
        if (cbWrite > cbThisWrite)
 
401
            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
 
402
 
 
403
        /* The rest must be read from the image. */
 
404
        cbReadImage = cbPostRead - cbWriteCopy - cbFill;
 
405
    }
 
406
 
 
407
    /* Read the entire data of the block so that we can compare whether it will
 
408
     * be modified by the write or not. */
 
409
    rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead,
 
410
                      pvTmp, cbPreRead + cbThisWrite + cbPostRead - cbFill);
 
411
    if (VBOX_FAILURE(rc))
 
412
        return rc;
 
413
 
 
414
    /* Check if the write would modify anything in this block. */
 
415
    if (   !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
 
416
        && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
 
417
                                    (char *)pvBuf + cbThisWrite, cbWriteCopy)))
 
418
    {
 
419
        /* Block is completely unchanged, so no need to write anything. */
 
420
        return VINF_SUCCESS;
 
421
    }
 
422
 
 
423
    /* Copy the data to the right place in the buffer. */
 
424
    memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
 
425
 
 
426
    /* Handle the data that goes after the write to fill the block. */
 
427
    if (cbPostRead)
 
428
    {
 
429
        /* Now assemble the remaining data. */
 
430
        if (cbWriteCopy)
 
431
            memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
 
432
                   (char *)pvBuf + cbThisWrite, cbWriteCopy);
 
433
        /* Zero out the remainder of this block. Will never be visible, as this
 
434
         * is beyond the limit of the image. */
 
435
        if (cbFill)
 
436
            memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
 
437
                   '\0', cbFill);
 
438
    }
 
439
 
 
440
    /* Write the full block to the virtual disk. */
 
441
    rc = pDisk->Backend->pfnWrite(pImage->pvBackendData,
 
442
                                  uOffset - cbPreRead, pvTmp,
 
443
                                  cbPreRead + cbThisWrite + cbPostRead,
 
444
                                  NULL,
 
445
                                  &cbPreRead, &cbPostRead);
 
446
    Assert(rc != VINF_VDI_BLOCK_FREE);
 
447
    Assert(cbPreRead == 0);
 
448
    Assert(cbPostRead == 0);
 
449
 
 
450
    return rc;
 
451
}
 
452
 
 
453
/**
 
454
 * Allocates and initializes an empty VBox HDD container.
 
455
 * No image files are opened.
 
456
 *
 
457
 * @returns VBox status code.
 
458
 * @param   pszBackend      Name of the image file backend to use.
 
459
 * @param   pfnError        Callback for setting extended error information.
 
460
 * @param   pvErrorUser     Opaque parameter for pfnError.
 
461
 * @param   ppDisk          Where to store the reference to the VBox HDD container.
 
462
 */
 
463
VBOXDDU_DECL(int) VDCreate(const char *pszBackend, PFNVDERROR pfnError,
 
464
                           void *pvErrorUser, PVBOXHDD *ppDisk)
 
465
{
 
466
    int rc = VINF_SUCCESS;
 
467
    PVBOXHDDBACKEND pBackend = NULL;
 
468
    PVBOXHDD pDisk = NULL;
 
469
 
 
470
    /* Passing an error callback is strictly not necessary any more. Any code
 
471
     * calling the HDD container functions should provide one, as otherwise
 
472
     * many detailed error messages will go unnoticed. If you find a situation
 
473
     * where you get no sensible error message from this code but you think
 
474
     * there should be one, shout loudly. There are no error messages for rare
 
475
     * and obvious error codes such as VERR_NO_MEMORY, and for situations which
 
476
     * the user cannot be made responsible for, such as program bugs causing
 
477
     * parameter checks to fail etc. */
 
478
    Assert(pfnError);
 
479
 
 
480
    /* Find backend. */
 
481
    for (unsigned i = 0; aBackends[i].pszBackendName != NULL; i++)
 
482
    {
 
483
        if (!strcmp(pszBackend, aBackends[i].pszBackendName))
 
484
        {
 
485
            pBackend = aBackends[i].Backend;
 
486
            break;
 
487
        }
 
488
    }
 
489
    if (pBackend)
 
490
    {
 
491
        pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
 
492
        if (pDisk)
 
493
        {
 
494
            pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
 
495
            pDisk->cImages      = 0;
 
496
            pDisk->pBase        = NULL;
 
497
            pDisk->pLast        = NULL;
 
498
            pDisk->cbSize       = 0;
 
499
            pDisk->cCylinders   = 0;
 
500
            pDisk->cHeads       = 0;
 
501
            pDisk->cSectors     = 0;
 
502
            pDisk->pfnError     = pfnError;
 
503
            pDisk->pvErrorUser  = pvErrorUser;
 
504
            pDisk->Backend      = pBackend;
 
505
            *ppDisk = pDisk;
 
506
        }
 
507
        else
 
508
            rc = VERR_NO_MEMORY;
 
509
    }
 
510
    else
 
511
        rc = vdEarlyError(pfnError, pvErrorUser, VERR_INVALID_PARAMETER,
 
512
                          RT_SRC_POS, "VD: unknown backend name '%s'",
 
513
                          pszBackend);
 
514
 
 
515
    LogFlow(("%s: returns %Vrc (pDisk=%#p)\n", __FUNCTION__, rc, pDisk));
 
516
    return rc;
 
517
}
 
518
 
 
519
/**
 
520
 * Destroys the VBox HDD container.
 
521
 * If container has opened image files they will be closed.
 
522
 *
 
523
 * @param   pDisk           Pointer to VBox HDD container.
 
524
 */
 
525
VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
 
526
{
 
527
    LogFlow(("%s: pDisk=%#p\n", pDisk));
 
528
    /* sanity check */
 
529
    Assert(pDisk);
 
530
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
531
 
 
532
    if (pDisk)
 
533
    {
 
534
        VDCloseAll(pDisk);
 
535
        RTMemFree(pDisk);
 
536
    }
 
537
}
 
538
 
 
539
/**
 
540
 * Opens an image file.
 
541
 *
 
542
 * The first opened image file in a HDD container must have a base image type,
 
543
 * others (next opened images) must be a differencing or undo images.
 
544
 * Linkage is checked for differencing image to be in consistence with the previously opened image.
 
545
 * When another differencing image is opened and the last image was opened in read/write access
 
546
 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
 
547
 * other processes to use images in read-only mode too.
 
548
 *
 
549
 * Note that the image can be opened in read-only mode if a read/write open is not possible.
 
550
 * Use VDIsReadOnly to check open mode.
 
551
 *
 
552
 * @returns VBox status code.
 
553
 * @param   pDisk           Pointer to VBox HDD container.
 
554
 * @param   pszFilename     Name of the image file to open.
 
555
 * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
 
556
 */
 
557
VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszFilename,
 
558
                         unsigned uOpenFlags)
 
559
{
 
560
    int rc = VINF_SUCCESS;
 
561
    LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__,
 
562
             pszFilename, uOpenFlags));
 
563
    /* sanity check */
 
564
    Assert(pDisk);
 
565
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
566
 
 
567
    /* Check arguments. */
 
568
    if (    !pszFilename
 
569
        ||  *pszFilename == '\0'
 
570
        ||  (uOpenFlags & ~VD_OPEN_FLAGS_MASK))
 
571
    {
 
572
        AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
 
573
        return VERR_INVALID_PARAMETER;
 
574
    }
 
575
 
 
576
    /* Force readonly for images without base/diff consistency checking. */
 
577
    if (uOpenFlags & VD_OPEN_FLAGS_INFO)
 
578
        uOpenFlags |= VD_OPEN_FLAGS_READONLY;
 
579
 
 
580
    /* Set up image descriptor. */
 
581
    PVDIMAGE pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
 
582
    if (!pImage)
 
583
        return VERR_NO_MEMORY;
 
584
    pImage->pszFilename = RTStrDup(pszFilename);
 
585
    if (!pImage->pszFilename)
 
586
        rc = VERR_NO_MEMORY;
 
587
 
 
588
    if (VBOX_SUCCESS(rc))
 
589
    {
 
590
        pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
 
591
        rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
 
592
                                     uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
 
593
                                     pDisk->pfnError, pDisk->pvErrorUser,
 
594
                                     &pImage->pvBackendData);
 
595
    }
 
596
    /* If the open in read-write mode failed, retry in read-only mode. */
 
597
    if (VBOX_FAILURE(rc))
 
598
    {
 
599
        if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
 
600
            &&  (rc == VERR_ACCESS_DENIED
 
601
                 || rc == VERR_PERMISSION_DENIED
 
602
                 || rc == VERR_WRITE_PROTECT
 
603
                 || rc == VERR_SHARING_VIOLATION
 
604
                 || rc == VERR_FILE_LOCK_FAILED))
 
605
            rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
 
606
                                           (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
 
607
                                         | VD_OPEN_FLAGS_READONLY,
 
608
                                         pDisk->pfnError, pDisk->pvErrorUser,
 
609
                                         &pImage->pvBackendData);
 
610
        if (VBOX_FAILURE(rc))
 
611
            rc = vdError(pDisk, rc, RT_SRC_POS,
 
612
                         N_("VD: error opening image file '%s'"), pszFilename);
 
613
    }
 
614
 
 
615
    if (VBOX_SUCCESS(rc))
 
616
    {
 
617
        VDIMAGETYPE enmImageType;
 
618
        rc = pDisk->Backend->pfnGetImageType(pImage->pvBackendData,
 
619
                                             &enmImageType);
 
620
        /* Check image type. As the image itself has no idea whether it's a
 
621
         * base image or not, this info is derived here. Image 0 can be fixed
 
622
         * or normal, all others must be normal images. */
 
623
        if (    VBOX_SUCCESS(rc)
 
624
            &&  !(uOpenFlags & VD_OPEN_FLAGS_INFO)
 
625
            &&  pDisk->cImages != 0
 
626
            &&  enmImageType != VD_IMAGE_TYPE_NORMAL)
 
627
            rc = VERR_VDI_INVALID_TYPE;
 
628
 
 
629
        /** @todo optionally check UUIDs */
 
630
 
 
631
        if (VBOX_SUCCESS(rc))
 
632
        {
 
633
            uint64_t cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
 
634
            if (pDisk->cImages == 0)
 
635
            {
 
636
                /* Cache disk information. */
 
637
                pDisk->cbSize = cbSize;
 
638
 
 
639
                /* Cache CHS geometry. */
 
640
                int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
 
641
                                                         &pDisk->cCylinders,
 
642
                                                         &pDisk->cHeads,
 
643
                                                         &pDisk->cSectors);
 
644
                if (VBOX_FAILURE(rc2))
 
645
                {
 
646
                    pDisk->cCylinders = 0;
 
647
                    pDisk->cHeads = 0;
 
648
                    pDisk->cSectors = 0;
 
649
                }
 
650
                else
 
651
                {
 
652
                    /* Make sure the CHS geometry is properly clipped. */
 
653
                    pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
 
654
                    pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
 
655
                    pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
 
656
                }
 
657
 
 
658
                /* Cache translation mode. */
 
659
                rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
 
660
                                                        &pDisk->enmTranslation);
 
661
                if (VBOX_FAILURE(rc2))
 
662
                    pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
 
663
            }
 
664
            else
 
665
            {
 
666
                /* Check image size/block size for consistency. */
 
667
                if (cbSize != pDisk->cbSize)
 
668
                    rc = VERR_VDI_INVALID_TYPE;
 
669
            }
 
670
        }
 
671
 
 
672
        if (VBOX_SUCCESS(rc) && pDisk->cImages != 0)
 
673
        {
 
674
            /* Switch previous image to read-only mode. */
 
675
            unsigned uOpenFlagsPrevImg;
 
676
            uOpenFlagsPrevImg = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
 
677
            if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
 
678
            {
 
679
                uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
 
680
                rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
 
681
            }
 
682
        }
 
683
 
 
684
        if (VBOX_SUCCESS(rc))
 
685
        {
 
686
            /* Image successfully opened, make it the last image. */
 
687
            vdAddImageToList(pDisk, pImage);
 
688
        }
 
689
        else
 
690
        {
 
691
            /* Error detected, but image opened. Close image. */
 
692
            int rc2;
 
693
            rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
 
694
            AssertRC(rc2);
 
695
            pImage->pvBackendData = NULL;
 
696
        }
 
697
    }
 
698
 
 
699
    if (VBOX_FAILURE(rc))
 
700
    {
 
701
        if (pImage)
 
702
        {
 
703
            if (pImage->pszFilename)
 
704
                RTStrFree(pImage->pszFilename);
 
705
            RTMemFree(pImage);
 
706
        }
 
707
    }
 
708
 
 
709
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
710
    return rc;
 
711
}
 
712
 
 
713
/**
 
714
 * Creates and opens a new base image file.
 
715
 *
 
716
 * @returns VBox status code.
 
717
 * @param   pDisk           Pointer to VBox HDD container.
 
718
 * @param   pszFilename     Name of the image file to create.
 
719
 * @param   enmType         Image type, only base image types are acceptable.
 
720
 * @param   cbSize          Image size in bytes.
 
721
 * @param   uImageFlags     Flags specifying special image features.
 
722
 * @param   pszComment      Pointer to image comment. NULL is ok.
 
723
 * @param   cCylinders      Number of cylinders (must be <= 16383).
 
724
 * @param   cHeads          Number of heads (must be <= 16).
 
725
 * @param   cSectors        Number of sectors (must be <= 63);
 
726
 * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
 
727
 * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
 
728
 * @param   pvUser          User argument for the progress callback.
 
729
 */
 
730
VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszFilename,
 
731
                               VDIMAGETYPE enmType, uint64_t cbSize,
 
732
                               unsigned uImageFlags, const char *pszComment,
 
733
                               unsigned cCylinders, unsigned cHeads,
 
734
                               unsigned cSectors, unsigned uOpenFlags,
 
735
                               PFNVMPROGRESS pfnProgress, void *pvUser)
 
736
{
 
737
    int rc = VINF_SUCCESS;
 
738
    LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__,
 
739
             pszFilename, uOpenFlags));
 
740
    /* sanity check */
 
741
    Assert(pDisk);
 
742
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
743
 
 
744
    /* Check arguments. */
 
745
    if (    !pszFilename
 
746
        ||  *pszFilename == '\0'
 
747
        ||  (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED)
 
748
        ||  !cbSize
 
749
        ||  (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
 
750
        ||  cCylinders == 0
 
751
        ||  cCylinders > 16383
 
752
        ||  cHeads == 0
 
753
        ||  cHeads > 16
 
754
        ||  cSectors == 0
 
755
        ||  cSectors > 63)
 
756
    {
 
757
        AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
 
758
        return VERR_INVALID_PARAMETER;
 
759
    }
 
760
 
 
761
    /* Check state. */
 
762
    if (pDisk->cImages != 0)
 
763
    {
 
764
        AssertMsgFailed(("Create base image cannot be done with other images open\n"));
 
765
        return VERR_VDI_INVALID_STATE;
 
766
    }
 
767
 
 
768
    /* Set up image descriptor. */
 
769
    PVDIMAGE pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
 
770
    if (!pImage)
 
771
        return VERR_NO_MEMORY;
 
772
    pImage->pszFilename = RTStrDup(pszFilename);
 
773
    if (!pImage->pszFilename)
 
774
        rc = VERR_NO_MEMORY;
 
775
 
 
776
    if (VBOX_SUCCESS(rc))
 
777
        rc = pDisk->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
 
778
                                       uImageFlags, pszComment, cCylinders,
 
779
                                       cHeads, cSectors, uOpenFlags,
 
780
                                       pfnProgress, pvUser,
 
781
                                       pDisk->pfnError, pDisk->pvErrorUser,
 
782
                                       &pImage->pvBackendData);
 
783
 
 
784
    if (VBOX_SUCCESS(rc))
 
785
    {
 
786
        /** @todo optionally check UUIDs */
 
787
 
 
788
        if (VBOX_SUCCESS(rc))
 
789
        {
 
790
            uint64_t cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
 
791
            if (pDisk->cImages == 0)
 
792
            {
 
793
                /* Cache disk information. */
 
794
                pDisk->cbSize = cbSize;
 
795
 
 
796
                /* Cache CHS geometry. */
 
797
                int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
 
798
                                                         &pDisk->cCylinders,
 
799
                                                         &pDisk->cHeads,
 
800
                                                         &pDisk->cSectors);
 
801
                if (VBOX_FAILURE(rc2))
 
802
                {
 
803
                    pDisk->cCylinders = 0;
 
804
                    pDisk->cHeads = 0;
 
805
                    pDisk->cSectors = 0;
 
806
                }
 
807
                else
 
808
                {
 
809
                    /* Make sure the CHS geometry is properly clipped. */
 
810
                    pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
 
811
                    pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
 
812
                    pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
 
813
                }
 
814
 
 
815
                /* Cache translation mode. */
 
816
                rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
 
817
                                                        &pDisk->enmTranslation);
 
818
                if (VBOX_FAILURE(rc2))
 
819
                    pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
 
820
            }
 
821
            else
 
822
            {
 
823
                /* Check image size/block size for consistency. */
 
824
                if (cbSize != pDisk->cbSize)
 
825
                    rc = VERR_VDI_INVALID_TYPE;
 
826
            }
 
827
        }
 
828
 
 
829
        if (VBOX_SUCCESS(rc))
 
830
        {
 
831
            /* Image successfully opened, make it the last image. */
 
832
            vdAddImageToList(pDisk, pImage);
 
833
        }
 
834
        else
 
835
        {
 
836
            /* Error detected, but image opened. Close and delete image. */
 
837
            int rc2;
 
838
            rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, true);
 
839
            AssertRC(rc2);
 
840
            pImage->pvBackendData = NULL;
 
841
        }
 
842
    }
 
843
 
 
844
    if (VBOX_FAILURE(rc))
 
845
    {
 
846
        if (pImage)
 
847
        {
 
848
            if (pImage->pszFilename)
 
849
                RTStrFree(pImage->pszFilename);
 
850
            RTMemFree(pImage);
 
851
        }
 
852
    }
 
853
 
 
854
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
855
    return rc;
 
856
}
 
857
 
 
858
/**
 
859
 * Creates and opens a new differencing image file in HDD container.
 
860
 * See comments for VDOpen function about differencing images.
 
861
 *
 
862
 * @returns VBox status code.
 
863
 * @param   pDisk           Pointer to VBox HDD container.
 
864
 * @param   pszFilename     Name of the differencing image file to create.
 
865
 * @param   uImageFlags     Flags specifying special image features.
 
866
 * @param   pszComment      Pointer to image comment. NULL is ok.
 
867
 * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
 
868
 * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
 
869
 * @param   pvUser          User argument for the progress callback.
 
870
 */
 
871
VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszFilename,
 
872
                               unsigned uImageFlags, const char *pszComment,
 
873
                               unsigned uOpenFlags,
 
874
                               PFNVMPROGRESS pfnProgress, void *pvUser)
 
875
{
 
876
    return VERR_NOT_IMPLEMENTED;
 
877
}
 
878
 
 
879
/**
 
880
 * Merges two images having a parent/child relationship (both directions).
 
881
 * As a side effect the source image is deleted from both the disk and
 
882
 * the images in the VBox HDD container.
 
883
 *
 
884
 * @returns VBox status code.
 
885
 * @param   pDisk           Pointer to VBox HDD container.
 
886
 * @param   nImageFrom      Name of the image file to merge from.
 
887
 * @param   nImageTo        Name of the image file to merge to.
 
888
 * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
 
889
 * @param   pvUser          User argument for the progress callback.
 
890
 */
 
891
VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
 
892
                          unsigned nImageTo, PFNVMPROGRESS pfnProgress,
 
893
                          void *pvUser)
 
894
{
 
895
    return VERR_NOT_IMPLEMENTED;
 
896
}
 
897
 
 
898
/**
 
899
 * Copies an image from one VBox HDD container to another.
 
900
 * The copy is opened in the target VBox HDD container.
 
901
 * It is possible to convert between different image formats, because the
 
902
 * backend for the destination VBox HDD container may be different from the
 
903
 * source container.
 
904
 * If both the source and destination reference the same VBox HDD container,
 
905
 * then the image is moved (by copying/deleting) to the new location.
 
906
 * The source container is unchanged if the move operation fails, otherwise
 
907
 * the image at the new location is opened in the same way as the old one was.
 
908
 *
 
909
 * @returns VBox status code.
 
910
 * @param   pDiskFrom       Pointer to source VBox HDD container.
 
911
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
912
 * @param   pDiskTo         Pointer to destination VBox HDD container.
 
913
 * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
 
914
 * @param   pvUser          User argument for the progress callback.
 
915
 */
 
916
VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
 
917
                         PFNVMPROGRESS pfnProgress, void *pvUser)
 
918
{
 
919
    return VERR_NOT_IMPLEMENTED;
 
920
}
 
921
 
 
922
/**
 
923
 * Compacts a growing image file by removing zeroed data blocks.
 
924
 * Optionally defragments data in the image so that ascending sector numbers
 
925
 * are stored in ascending location in the image file.
 
926
 *
 
927
 * @todo maybe include this function in VDCopy.
 
928
 *
 
929
 * @returns VBox status code.
 
930
 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
 
931
 * @param   pDisk           Pointer to VBox HDD container.
 
932
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
933
 * @param   fDefragment     If true, reorder file data so that sectors are stored in ascending order.
 
934
 * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
 
935
 * @param   pvUser          User argument for the progress callback.
 
936
 */
 
937
VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
 
938
                            bool fDefragment,
 
939
                            PFNVMPROGRESS pfnProgress, void *pvUser)
 
940
{
 
941
    return VERR_NOT_IMPLEMENTED;
 
942
}
 
943
 
 
944
/**
 
945
 * Resizes an image. Allows setting the disk size to both larger and smaller
 
946
 * values than the current disk size.
 
947
 *
 
948
 * @returns VBox status code.
 
949
 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
 
950
 * @param   pDisk           Pointer to VBox HDD container.
 
951
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
952
 * @param   cbSize          New image size in bytes.
 
953
 * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
 
954
 * @param   pvUser          User argument for the progress callback.
 
955
 */
 
956
VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, unsigned nImage, uint64_t cbSize,
 
957
                           PFNVMPROGRESS pfnProgress, void *pvUser)
 
958
{
 
959
    return VERR_NOT_IMPLEMENTED;
 
960
}
 
961
 
 
962
/**
 
963
 * Closes the last opened image file in the HDD container. Leaves all changes inside it.
 
964
 * If previous image file was opened in read-only mode (that is normal) and closing image
 
965
 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
 
966
 * will be reopened in read/write mode.
 
967
 *
 
968
 * @param   pDisk           Pointer to VBox HDD container.
 
969
 * @param   fDelete         If true, delete the image from the host disk.
 
970
 */
 
971
VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
 
972
{
 
973
    LogFlow(("%s: fDelete=%d\n", __FUNCTION__, fDelete));
 
974
    /* sanity check */
 
975
    Assert(pDisk);
 
976
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
977
 
 
978
    PVDIMAGE pImage = pDisk->pLast;
 
979
    unsigned uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
 
980
    /* Remove image from list of opened images. */
 
981
    vdRemoveImageFromList(pDisk, pImage);
 
982
    /* Close (and optionally delete) image. */
 
983
    int rc = pDisk->Backend->pfnClose(pImage->pvBackendData, fDelete);
 
984
    /* Free remaining resources related to the image. */
 
985
    RTStrFree(pImage->pszFilename);
 
986
    RTMemFree(pImage);
 
987
 
 
988
    /* If disk was previously in read/write mode, make sure it will stay like
 
989
     * this after closing this image. Set the open flags accordingly. */
 
990
    if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
 
991
    {
 
992
        uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
 
993
        uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
 
994
        rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData,
 
995
                                                     uOpenFlags);
 
996
    }
 
997
 
 
998
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
999
    return rc;
 
1000
}
 
1001
 
 
1002
/**
 
1003
 * Closes all opened image files in HDD container.
 
1004
 *
 
1005
 * @param   pDisk           Pointer to VDI HDD container.
 
1006
 */
 
1007
VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
 
1008
{
 
1009
    LogFlow(("%s:\n", __FUNCTION__));
 
1010
    /* sanity check */
 
1011
    Assert(pDisk);
 
1012
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1013
 
 
1014
    PVDIMAGE pImage = pDisk->pLast;
 
1015
    int rc = VINF_SUCCESS;
 
1016
    while (pImage)
 
1017
    {
 
1018
        PVDIMAGE pPrev = pImage->pPrev;
 
1019
        /* Remove image from list of opened images. */
 
1020
        vdRemoveImageFromList(pDisk, pImage);
 
1021
        /* Close image. */
 
1022
        int rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
 
1023
        if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
 
1024
            rc = rc2;
 
1025
        /* Free remaining resources related to the image. */
 
1026
        RTStrFree(pImage->pszFilename);
 
1027
        RTMemFree(pImage);
 
1028
        pImage = pPrev;
 
1029
    }
 
1030
    Assert(pDisk->pLast == NULL);
 
1031
 
 
1032
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1033
    return rc;
 
1034
}
 
1035
 
 
1036
/**
 
1037
 * Read data from virtual HDD.
 
1038
 *
 
1039
 * @returns VBox status code.
 
1040
 * @param   pDisk           Pointer to VBox HDD container.
 
1041
 * @param   uOffset         Offset of first reading byte from start of disk.
 
1042
 * @param   pvBuf           Pointer to buffer for reading data.
 
1043
 * @param   cbRead          Number of bytes to read.
 
1044
 */
 
1045
VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf, size_t cbRead)
 
1046
{
 
1047
    /* sanity check */
 
1048
    LogFlow(("%s: offset=%llu cbRead=%u\n", __FUNCTION__, uOffset, (unsigned)cbRead));
 
1049
    Assert(pDisk);
 
1050
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1051
 
 
1052
    int rc = VINF_SUCCESS;
 
1053
    PVDIMAGE pImage = pDisk->pLast;
 
1054
    if (RT_UNLIKELY(!pImage))
 
1055
    {
 
1056
        Assert(pImage);
 
1057
        rc = VERR_VDI_NOT_OPENED;
 
1058
        goto out;
 
1059
    }
 
1060
 
 
1061
    /* Check params. */
 
1062
    if (uOffset + cbRead > pDisk->cbSize || cbRead == 0)
 
1063
    {
 
1064
        AssertMsgFailed(("uOffset=%llu cbRead=%u\n", uOffset, cbRead));
 
1065
        return VERR_INVALID_PARAMETER;
 
1066
    }
 
1067
 
 
1068
    rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
 
1069
out:
 
1070
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1071
    return rc;
 
1072
}
 
1073
 
 
1074
/**
 
1075
 * Write data to virtual HDD.
 
1076
 *
 
1077
 * @returns VBox status code.
 
1078
 * @param   pDisk           Pointer to VBox HDD container.
 
1079
 * @param   uOffset         Offset of first reading byte from start of disk.
 
1080
 * @param   pvBuf           Pointer to buffer for writing data.
 
1081
 * @param   cbWrite         Number of bytes to write.
 
1082
 */
 
1083
VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf, size_t cbWrite)
 
1084
{
 
1085
    /* sanity check */
 
1086
    Assert(pDisk);
 
1087
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1088
 
 
1089
    int rc = VINF_SUCCESS;
 
1090
    size_t cbThisWrite;
 
1091
    size_t cbPreRead, cbPostRead;
 
1092
    PVDIMAGE pImage = pDisk->pLast;
 
1093
    if (RT_UNLIKELY(!pImage))
 
1094
    {
 
1095
        Assert(pImage);
 
1096
        rc = VERR_VDI_NOT_OPENED;
 
1097
        goto out;
 
1098
    }
 
1099
 
 
1100
    /* Check params. */
 
1101
    if (uOffset + cbWrite > pDisk->cbSize || cbWrite == 0)
 
1102
    {
 
1103
        AssertMsgFailed(("uOffset=%llu cbWrite=%u\n", uOffset, cbWrite));
 
1104
        rc = VERR_INVALID_PARAMETER;
 
1105
        goto out;
 
1106
    }
 
1107
 
 
1108
    vdSetModifiedFlag(pDisk);
 
1109
 
 
1110
    /* Loop until all written. */
 
1111
    do
 
1112
    {
 
1113
        /* Try to write the possibly partial block to the last opened image.
 
1114
         * This works when the block is already allocated in this image or
 
1115
         * if it is a full-block write, which automatically allocates a new
 
1116
         * block if needed. */
 
1117
        cbThisWrite = cbWrite;
 
1118
        rc = pDisk->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf,
 
1119
                                      cbThisWrite, &cbThisWrite,
 
1120
                                      &cbPreRead, &cbPostRead);
 
1121
        if (rc == VINF_VDI_BLOCK_FREE)
 
1122
        {
 
1123
            void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
 
1124
            if (!pvBuf)
 
1125
            {
 
1126
                Assert(!pvBuf);
 
1127
                rc = VERR_NO_MEMORY;
 
1128
                break;
 
1129
            }
 
1130
 
 
1131
            if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
 
1132
            {
 
1133
                /* Optimized write, suppress writing to a so far unallocated
 
1134
                 * block when the data is identical than as of the parent. */
 
1135
                rc = vdWriteHelperOptimized(pDisk, pImage, uOffset,
 
1136
                                            cbWrite, cbThisWrite,
 
1137
                                            cbPreRead, cbPostRead,
 
1138
                                            pvBuf, pvTmp);
 
1139
            }
 
1140
            else
 
1141
            {
 
1142
                /* Normal write, not optimized in any way. The block will be
 
1143
                 * written no matter what. This will usually (unless the
 
1144
                 * backend has some further optimization enabled) cause the
 
1145
                 * block to be allocated. */
 
1146
                rc = vdWriteHelperStandard(pDisk, pImage, uOffset,
 
1147
                                           cbWrite, cbThisWrite,
 
1148
                                           cbPreRead, cbPostRead,
 
1149
                                           pvBuf, pvTmp);
 
1150
            }
 
1151
            RTMemTmpFree(pvTmp);
 
1152
            if (VBOX_FAILURE(rc))
 
1153
                break;
 
1154
        }
 
1155
 
 
1156
        cbWrite -= cbThisWrite;
 
1157
        uOffset += cbThisWrite;
 
1158
        pvBuf = (char *)pvBuf + cbThisWrite;
 
1159
    } while (cbWrite != 0 && VBOX_SUCCESS(rc));
 
1160
 
 
1161
out:
 
1162
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1163
    return rc;
 
1164
}
 
1165
 
 
1166
/**
 
1167
 * Make sure the on disk representation of a virtual HDD is up to date.
 
1168
 *
 
1169
 * @returns VBox status code.
 
1170
 * @param   pDisk           Pointer to VBox HDD container.
 
1171
 */
 
1172
VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
 
1173
{
 
1174
    /* sanity check */
 
1175
    Assert(pDisk);
 
1176
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1177
 
 
1178
    int rc = VINF_SUCCESS;
 
1179
    PVDIMAGE pImage = pDisk->pLast;
 
1180
    if (RT_UNLIKELY(!pImage))
 
1181
    {
 
1182
        Assert(pImage);
 
1183
        rc = VERR_VDI_NOT_OPENED;
 
1184
    }
 
1185
    else
 
1186
    {
 
1187
        vdResetModifiedFlag(pDisk);
 
1188
        rc = pDisk->Backend->pfnFlush(pImage->pvBackendData);
 
1189
    }
 
1190
 
 
1191
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1192
    return rc;
 
1193
}
 
1194
 
 
1195
/**
 
1196
 * Get number of opened images in HDD container.
 
1197
 *
 
1198
 * @returns Number of opened images for HDD container. 0 if no images have been opened.
 
1199
 * @param   pDisk           Pointer to VBox HDD container.
 
1200
 */
 
1201
VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
 
1202
{
 
1203
    /* sanity check */
 
1204
    Assert(pDisk);
 
1205
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1206
 
 
1207
    unsigned c = pDisk->cImages;
 
1208
    LogFlow(("%s: returns %d\n", __FUNCTION__, c));
 
1209
    return c;
 
1210
}
 
1211
 
 
1212
/**
 
1213
 * Get read/write mode of the VBox HDD container.
 
1214
 *
 
1215
 * @returns Virtual disk ReadOnly status.
 
1216
 * @returns true if no image is opened in HDD container.
 
1217
 * @param   pDisk           Pointer to VBox HDD container.
 
1218
 */
 
1219
VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
 
1220
{
 
1221
    /* sanity check */
 
1222
    Assert(pDisk);
 
1223
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1224
 
 
1225
    bool f;
 
1226
    if (pDisk->pLast)
 
1227
    {
 
1228
        unsigned uOpenFlags;
 
1229
        uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
 
1230
        f = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
 
1231
    }
 
1232
    else
 
1233
    {
 
1234
        AssertMsgFailed(("No disk image is opened!\n"));
 
1235
        f = true;
 
1236
    }
 
1237
 
 
1238
    LogFlow(("%s: returns %d\n", __FUNCTION__, f));
 
1239
    return f;
 
1240
}
 
1241
 
 
1242
/**
 
1243
 * Get total disk size of the VBox HDD container.
 
1244
 *
 
1245
 * @returns Virtual disk size in bytes.
 
1246
 * @returns 0 if no image is opened in HDD container.
 
1247
 * @param   pDisk           Pointer to VBox HDD container.
 
1248
 */
 
1249
VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk)
 
1250
{
 
1251
    /* sanity check */
 
1252
    Assert(pDisk);
 
1253
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1254
 
 
1255
    uint64_t cb = pDisk->cbSize;
 
1256
    LogFlow(("%s: returns %lld\n", __FUNCTION__, cb));
 
1257
    return cb;
 
1258
}
 
1259
 
 
1260
/**
 
1261
 * Get virtual disk geometry stored in HDD container.
 
1262
 *
 
1263
 * @returns VBox status code.
 
1264
 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
 
1265
 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
 
1266
 * @param   pDisk           Pointer to VBox HDD container.
 
1267
 * @param   pcCylinders     Where to store the number of cylinders. NULL is ok.
 
1268
 * @param   pcHeads         Where to store the number of heads. NULL is ok.
 
1269
 * @param   pcSectors       Where to store the number of sectors. NULL is ok.
 
1270
 */
 
1271
VBOXDDU_DECL(int) VDGetGeometry(PVBOXHDD pDisk, unsigned *pcCylinders,
 
1272
                                unsigned *pcHeads, unsigned *pcSectors)
 
1273
{
 
1274
    /* sanity check */
 
1275
    Assert(pDisk);
 
1276
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1277
 
 
1278
    int rc = VINF_SUCCESS;
 
1279
    PVDIMAGE pImage = pDisk->pBase;
 
1280
    if (RT_UNLIKELY(!pImage))
 
1281
    {
 
1282
        Assert(pImage);
 
1283
        rc = VERR_VDI_NOT_OPENED;
 
1284
    }
 
1285
    else
 
1286
    {
 
1287
        if (pDisk->cCylinders != 0)
 
1288
        {
 
1289
            if (pcCylinders)
 
1290
                *pcCylinders = pDisk->cCylinders;
 
1291
            if (pcHeads)
 
1292
                *pcHeads = pDisk->cHeads;
 
1293
            if (pcSectors)
 
1294
                *pcSectors = pDisk->cSectors;
 
1295
        }
 
1296
        else
 
1297
            rc = VERR_VDI_GEOMETRY_NOT_SET;
 
1298
    }
 
1299
    LogFlow(("%s: %Vrc (CHS=%u/%u/%u)\n", __FUNCTION__, rc,
 
1300
             pDisk->cCylinders, pDisk->cHeads, pDisk->cSectors));
 
1301
    return rc;
 
1302
}
 
1303
 
 
1304
/**
 
1305
 * Store virtual disk geometry in HDD container.
 
1306
 *
 
1307
 * Note that in case of unrecoverable error all images in HDD container will be closed.
 
1308
 *
 
1309
 * @returns VBox status code.
 
1310
 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
 
1311
 * @param   pDisk           Pointer to VBox HDD container.
 
1312
 * @param   cCylinders      Number of cylinders.
 
1313
 * @param   cHeads          Number of heads.
 
1314
 * @param   cSectors        Number of sectors.
 
1315
 */
 
1316
VBOXDDU_DECL(int) VDSetGeometry(PVBOXHDD pDisk, unsigned cCylinders,
 
1317
                                unsigned cHeads, unsigned cSectors)
 
1318
{
 
1319
    /* sanity check */
 
1320
    Assert(pDisk);
 
1321
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1322
 
 
1323
    int rc = VINF_SUCCESS;
 
1324
    PVDIMAGE pImage = pDisk->pBase;
 
1325
    if (RT_UNLIKELY(!pImage))
 
1326
    {
 
1327
        Assert(pImage);
 
1328
        rc = VERR_VDI_NOT_OPENED;
 
1329
    }
 
1330
    else
 
1331
    {
 
1332
        if (    cCylinders != pDisk->cCylinders
 
1333
            ||  cHeads != pDisk->cHeads
 
1334
            ||  cSectors != pDisk->cSectors)
 
1335
        {
 
1336
            /* Only update geometry if it is changed. Avoids similar checks
 
1337
             * in every backend. Most of the time the new geometry is set to
 
1338
             * the previous values, so no need to go through the hassle of
 
1339
             * updating an image which could be opened in read-only mode right
 
1340
             * now. */
 
1341
            rc = pDisk->Backend->pfnSetGeometry(pImage->pvBackendData,
 
1342
                                                cCylinders, cHeads, cSectors);
 
1343
 
 
1344
            /* Cache new geometry values in any case, whether successful or not. */
 
1345
            int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
 
1346
                                                     &pDisk->cCylinders,
 
1347
                                                     &pDisk->cHeads,
 
1348
                                                     &pDisk->cSectors);
 
1349
            if (VBOX_FAILURE(rc2))
 
1350
            {
 
1351
                pDisk->cCylinders = 0;
 
1352
                pDisk->cHeads = 0;
 
1353
                pDisk->cSectors = 0;
 
1354
            }
 
1355
            else
 
1356
            {
 
1357
                /* Make sure the CHS geometry is properly clipped. */
 
1358
                pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
 
1359
                pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
 
1360
                pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
 
1361
            }
 
1362
        }
 
1363
    }
 
1364
 
 
1365
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1366
    return rc;
 
1367
}
 
1368
 
 
1369
/**
 
1370
 * Get virtual disk translation mode stored in HDD container.
 
1371
 *
 
1372
 * @returns VBox status code.
 
1373
 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
 
1374
 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
 
1375
 * @param   pDisk           Pointer to VBox HDD container.
 
1376
 * @param   penmTranslation Where to store the translation mode (see pdm.h).
 
1377
 */
 
1378
VBOXDDU_DECL(int) VDGetTranslation(PVBOXHDD pDisk,
 
1379
                                   PPDMBIOSTRANSLATION penmTranslation)
 
1380
{
 
1381
    /* sanity check */
 
1382
    Assert(pDisk);
 
1383
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1384
 
 
1385
    int rc = VINF_SUCCESS;
 
1386
    PVDIMAGE pImage = pDisk->pBase;
 
1387
    if (RT_UNLIKELY(!pImage))
 
1388
    {
 
1389
        Assert(pImage);
 
1390
        rc = VERR_VDI_NOT_OPENED;
 
1391
    }
 
1392
    else
 
1393
    {
 
1394
        if (pDisk->enmTranslation != 0)
 
1395
            *penmTranslation = pDisk->enmTranslation;
 
1396
        else
 
1397
            rc = VERR_VDI_GEOMETRY_NOT_SET;
 
1398
    }
 
1399
    LogFlow(("%s: %Vrc (translation=%u)\n", __FUNCTION__, rc,
 
1400
             pDisk->enmTranslation));
 
1401
    return rc;
 
1402
}
 
1403
 
 
1404
/**
 
1405
 * Store virtual disk translation mode in HDD container.
 
1406
 *
 
1407
 * Note that in case of unrecoverable error all images in HDD container will be closed.
 
1408
 *
 
1409
 * @returns VBox status code.
 
1410
 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
 
1411
 * @param   pDisk           Pointer to VBox HDD container.
 
1412
 * @param   enmTranslation  Translation mode (see pdm.h).
 
1413
 */
 
1414
VBOXDDU_DECL(int) VDSetTranslation(PVBOXHDD pDisk,
 
1415
                                   PDMBIOSTRANSLATION enmTranslation)
 
1416
{
 
1417
    /* sanity check */
 
1418
    Assert(pDisk);
 
1419
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1420
 
 
1421
    int rc = VINF_SUCCESS;
 
1422
    PVDIMAGE pImage = pDisk->pBase;
 
1423
    if (RT_UNLIKELY(!pImage))
 
1424
    {
 
1425
        Assert(pImage);
 
1426
        rc = VERR_VDI_NOT_OPENED;
 
1427
    }
 
1428
    else
 
1429
    {
 
1430
        if (enmTranslation == 0)
 
1431
            rc = VERR_INVALID_PARAMETER;
 
1432
        else if (enmTranslation != pDisk->enmTranslation)
 
1433
        {
 
1434
            /* Only update translation mode if it is changed. Avoids similar
 
1435
             * checks in every backend. Most of the time the new translation
 
1436
             * mode is set to the previous value, so no need to go through the
 
1437
             * hassle of updating an image which could be opened in read-only
 
1438
             * mode right now. */
 
1439
            rc = pDisk->Backend->pfnSetTranslation(pImage->pvBackendData,
 
1440
                                                   enmTranslation);
 
1441
 
 
1442
            /* Cache new translation mode in any case, whether successful or not. */
 
1443
            int rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
 
1444
                                                        &pDisk->enmTranslation);
 
1445
            if (VBOX_FAILURE(rc2))
 
1446
                pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
 
1447
        }
 
1448
    }
 
1449
 
 
1450
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1451
    return rc;
 
1452
}
 
1453
 
 
1454
/**
 
1455
 * Get version of image in HDD container.
 
1456
 *
 
1457
 * @returns VBox status code.
 
1458
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1459
 * @param   pDisk           Pointer to VBox HDD container.
 
1460
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1461
 * @param   puVersion       Where to store the image version.
 
1462
 */
 
1463
VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
 
1464
                               unsigned *puVersion)
 
1465
{
 
1466
    return VERR_NOT_IMPLEMENTED;
 
1467
}
 
1468
 
 
1469
/**
 
1470
 * Get type of image in HDD container.
 
1471
 *
 
1472
 * @returns VBox status code.
 
1473
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1474
 * @param   pDisk           Pointer to VBox HDD container.
 
1475
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1476
 * @param   penmType        Where to store the image type.
 
1477
 */
 
1478
VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
 
1479
                                 PVDIMAGETYPE penmType)
 
1480
{
 
1481
    return VERR_NOT_IMPLEMENTED;
 
1482
}
 
1483
 
 
1484
/**
 
1485
 * Get flags of image in HDD container.
 
1486
 *
 
1487
 * @returns VBox status code.
 
1488
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1489
 * @param   pDisk           Pointer to VBox HDD container.
 
1490
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1491
 * @param   puImageFlags    Where to store the image flags.
 
1492
 */
 
1493
VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
 
1494
                                  unsigned *puImageFlags)
 
1495
{
 
1496
    return VERR_NOT_IMPLEMENTED;
 
1497
}
 
1498
 
 
1499
/**
 
1500
 * Get open flags of last opened image in HDD container.
 
1501
 *
 
1502
 * @returns VBox status code.
 
1503
 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
 
1504
 * @param   pDisk           Pointer to VBox HDD container.
 
1505
 * @param   puOpenFlags     Where to store the image open flags.
 
1506
 */
 
1507
VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned *puOpenFlags)
 
1508
{
 
1509
    /* sanity check */
 
1510
    Assert(pDisk);
 
1511
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1512
 
 
1513
    unsigned uOpenFlags = 0;
 
1514
    int rc = VINF_SUCCESS;
 
1515
    PVDIMAGE pImage = pDisk->pLast;
 
1516
    if (RT_UNLIKELY(!pImage))
 
1517
    {
 
1518
        Assert(pImage);
 
1519
        rc = VERR_VDI_NOT_OPENED;
 
1520
    }
 
1521
    else
 
1522
    {
 
1523
        uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
 
1524
        *puOpenFlags = uOpenFlags;
 
1525
    }
 
1526
    LogFlow(("%s: returns %Vrc uOpenFlags=%#x\n", __FUNCTION__, rc, uOpenFlags));
 
1527
    return uOpenFlags;
 
1528
}
 
1529
 
 
1530
/**
 
1531
 * Set open flags of last opened image in HDD container.
 
1532
 * This operation may cause file locking changes and/or files being reopened.
 
1533
 * Note that in case of unrecoverable error all images in HDD container will be closed.
 
1534
 *
 
1535
 * @returns Virtual disk block size in bytes.
 
1536
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1537
 * @returns VBox status code.
 
1538
 * @param   pDisk           Pointer to VBox HDD container.
 
1539
 * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
 
1540
 */
 
1541
VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned uOpenFlags)
 
1542
{
 
1543
    /* sanity check */
 
1544
    Assert(pDisk);
 
1545
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1546
 
 
1547
    int rc = VINF_SUCCESS;
 
1548
    PVDIMAGE pImage = pDisk->pLast;
 
1549
    if (RT_UNLIKELY(!pImage))
 
1550
    {
 
1551
        Assert(pImage);
 
1552
        rc = VERR_VDI_NOT_OPENED;
 
1553
    }
 
1554
    else
 
1555
        rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData,
 
1556
                                             uOpenFlags);
 
1557
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1558
    return rc;
 
1559
}
 
1560
 
 
1561
/**
 
1562
 * Get base filename of image in HDD container. Some image formats use
 
1563
 * other filenames as well, so don't use this for anything but for informational
 
1564
 * purposes.
 
1565
 *
 
1566
 * @returns VBox status code.
 
1567
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1568
 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
 
1569
 * @param   pDisk           Pointer to VBox HDD container.
 
1570
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1571
 * @param   pszFilename     Where to store the image file name.
 
1572
 * @param   cbFilename      Size of buffer pszFilename points to.
 
1573
 */
 
1574
VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
 
1575
                                char *pszFilename, unsigned cbFilename)
 
1576
{
 
1577
    return VERR_NOT_IMPLEMENTED;
 
1578
}
 
1579
 
 
1580
/**
 
1581
 * Get the comment line of image in HDD container.
 
1582
 *
 
1583
 * @returns VBox status code.
 
1584
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1585
 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
 
1586
 * @param   pDisk           Pointer to VBox HDD container.
 
1587
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1588
 * @param   pszComment      Where to store the comment string of image. NULL is ok.
 
1589
 * @param   cbComment       The size of pszComment buffer. 0 is ok.
 
1590
 */
 
1591
VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
 
1592
                               char *pszComment, unsigned cbComment)
 
1593
{
 
1594
    /* sanity check */
 
1595
    Assert(pDisk);
 
1596
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1597
    Assert(pszComment);
 
1598
 
 
1599
    PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
 
1600
    int rc;
 
1601
    if (pImage)
 
1602
        rc = pDisk->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
 
1603
                                           cbComment);
 
1604
    else
 
1605
        rc = VERR_VDI_IMAGE_NOT_FOUND;
 
1606
 
 
1607
    LogFlow(("%s: returns %Vrc, comment='%s' nImage=%u\n", __FUNCTION__,
 
1608
             rc, pszComment, nImage));
 
1609
    return rc;
 
1610
}
 
1611
 
 
1612
/**
 
1613
 * Changes the comment line of image in HDD container.
 
1614
 *
 
1615
 * @returns VBox status code.
 
1616
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1617
 * @param   pDisk           Pointer to VBox HDD container.
 
1618
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1619
 * @param   pszComment      New comment string (UTF-8). NULL is allowed to reset the comment.
 
1620
 */
 
1621
VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
 
1622
                               const char *pszComment)
 
1623
{
 
1624
    /* sanity check */
 
1625
    Assert(pDisk);
 
1626
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1627
    LogFlow(("%s: comment='%s' nImage=%u\n", __FUNCTION__, pszComment, nImage));
 
1628
 
 
1629
    PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
 
1630
    int rc;
 
1631
    if (pImage)
 
1632
        rc = pDisk->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
 
1633
    else
 
1634
        rc = VERR_VDI_IMAGE_NOT_FOUND;
 
1635
 
 
1636
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1637
    return rc;
 
1638
}
 
1639
 
 
1640
 
 
1641
/**
 
1642
 * Get UUID of image in HDD container.
 
1643
 *
 
1644
 * @returns VBox status code.
 
1645
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1646
 * @param   pDisk           Pointer to VBox HDD container.
 
1647
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1648
 * @param   pUuid           Where to store the image creation UUID.
 
1649
 */
 
1650
VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
 
1651
{
 
1652
    /* sanity check */
 
1653
    Assert(pDisk);
 
1654
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1655
    Assert(pUuid);
 
1656
 
 
1657
    PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
 
1658
    int rc;
 
1659
    if (pImage)
 
1660
        rc = pDisk->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
 
1661
    else
 
1662
        rc = VERR_VDI_IMAGE_NOT_FOUND;
 
1663
 
 
1664
    LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
 
1665
             rc, pUuid, nImage));
 
1666
    return rc;
 
1667
}
 
1668
 
 
1669
/**
 
1670
 * Set the image's UUID. Should not be used by normal applications.
 
1671
 *
 
1672
 * @returns VBox status code.
 
1673
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1674
 * @param   pDisk           Pointer to VBox HDD container.
 
1675
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1676
 * @param   pUuid           Optional parameter, new UUID of the image.
 
1677
 */
 
1678
VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
 
1679
{
 
1680
    LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
 
1681
    /* sanity check */
 
1682
    Assert(pDisk);
 
1683
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1684
    Assert(pUuid);
 
1685
 
 
1686
    PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
 
1687
    int rc;
 
1688
    if (pImage)
 
1689
        rc = pDisk->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
 
1690
    else
 
1691
        rc = VERR_VDI_IMAGE_NOT_FOUND;
 
1692
 
 
1693
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1694
    return rc;
 
1695
}
 
1696
 
 
1697
/**
 
1698
 * Get last modification UUID of image in HDD container.
 
1699
 *
 
1700
 * @returns VBox status code.
 
1701
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1702
 * @param   pDisk           Pointer to VBox HDD container.
 
1703
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1704
 * @param   pUuid           Where to store the image modification UUID.
 
1705
 */
 
1706
VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
 
1707
{
 
1708
    /* sanity check */
 
1709
    Assert(pDisk);
 
1710
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1711
    Assert(pUuid);
 
1712
 
 
1713
    PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
 
1714
    int rc;
 
1715
    if (pImage)
 
1716
        rc = pDisk->Backend->pfnGetModificationUuid(pImage->pvBackendData, pUuid);
 
1717
    else
 
1718
        rc = VERR_VDI_IMAGE_NOT_FOUND;
 
1719
 
 
1720
    LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
 
1721
             rc, pUuid, nImage));
 
1722
    return rc;
 
1723
}
 
1724
 
 
1725
/**
 
1726
 * Set the image's last modification UUID. Should not be used by normal applications.
 
1727
 *
 
1728
 * @returns VBox status code.
 
1729
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1730
 * @param   pDisk           Pointer to VBox HDD container.
 
1731
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1732
 * @param   pUuid           Optional parameter, new last modification UUID of the image.
 
1733
 */
 
1734
VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
 
1735
{
 
1736
    LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
 
1737
    /* sanity check */
 
1738
    Assert(pDisk);
 
1739
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1740
    Assert(pUuid);
 
1741
 
 
1742
    PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
 
1743
    int rc;
 
1744
    if (pImage)
 
1745
        rc = pDisk->Backend->pfnSetModificationUuid(pImage->pvBackendData, pUuid);
 
1746
    else
 
1747
        rc = VERR_VDI_IMAGE_NOT_FOUND;
 
1748
 
 
1749
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1750
    return rc;
 
1751
}
 
1752
 
 
1753
/**
 
1754
 * Get parent UUID of image in HDD container.
 
1755
 *
 
1756
 * @returns VBox status code.
 
1757
 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
 
1758
 * @param   pDisk           Pointer to VBox HDD container.
 
1759
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1760
 * @param   pUuid           Where to store the parent image UUID.
 
1761
 */
 
1762
VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
 
1763
                                  PRTUUID pUuid)
 
1764
{
 
1765
    /* sanity check */
 
1766
    Assert(pDisk);
 
1767
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1768
    Assert(pUuid);
 
1769
 
 
1770
    PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
 
1771
    int rc;
 
1772
    if (pImage)
 
1773
        rc = pDisk->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
 
1774
    else
 
1775
        rc = VERR_VDI_IMAGE_NOT_FOUND;
 
1776
 
 
1777
    LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
 
1778
             rc, pUuid, nImage));
 
1779
    return rc;
 
1780
}
 
1781
 
 
1782
/**
 
1783
 * Set the image's parent UUID. Should not be used by normal applications.
 
1784
 *
 
1785
 * @returns VBox status code.
 
1786
 * @param   pDisk           Pointer to VBox HDD container.
 
1787
 * @param   nImage          Image number, counts from 0. 0 is always base image of container.
 
1788
 * @param   pUuid           Optional parameter, new parent UUID of the image.
 
1789
 */
 
1790
VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
 
1791
                                  PCRTUUID pUuid)
 
1792
{
 
1793
    LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
 
1794
    /* sanity check */
 
1795
    Assert(pDisk);
 
1796
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1797
    Assert(pUuid);
 
1798
 
 
1799
    PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
 
1800
    int rc;
 
1801
    if (pImage)
 
1802
        rc = pDisk->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
 
1803
    else
 
1804
        rc = VERR_VDI_IMAGE_NOT_FOUND;
 
1805
 
 
1806
    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
 
1807
    return rc;
 
1808
}
 
1809
 
 
1810
 
 
1811
/**
 
1812
 * Debug helper - dumps all opened images in HDD container into the log file.
 
1813
 *
 
1814
 * @param   pDisk           Pointer to VDI HDD container.
 
1815
 */
 
1816
VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
 
1817
{
 
1818
    /* sanity check */
 
1819
    Assert(pDisk);
 
1820
    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
 
1821
 
 
1822
    RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
 
1823
    for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
 
1824
    {
 
1825
        RTLogPrintf("Dumping VDI image \"%s\" file=%#p\n", pImage->pszFilename);
 
1826
        /** @todo call backend to print its part. */
 
1827
    }
 
1828
}
 
1829