MFC r268236,268264,268524,268646,268802,269021:
This brings VHD support to mkimg(1); both dynamic and fixed file formats. Dynamic VHD and VMDK file images are now sparsely written, meaning that "free" sectors do not occupy space. Relnotes: yes
This commit is contained in:
parent
920342975e
commit
37a9f7be2f
@ -9,6 +9,7 @@ CFLAGS+=-DSPARSE_WRITE
|
||||
# List of formats to support
|
||||
SRCS+= \
|
||||
raw.c \
|
||||
vhd.c \
|
||||
vmdk.c
|
||||
|
||||
# List of schemes to support
|
||||
|
@ -39,6 +39,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include "mkimg.h"
|
||||
#include "scheme.h"
|
||||
|
||||
#ifndef APM_ENT_TYPE_APPLE_BOOT
|
||||
#define APM_ENT_TYPE_APPLE_BOOT "Apple_Bootstrap"
|
||||
#endif
|
||||
#ifndef APM_ENT_TYPE_FREEBSD_NANDFS
|
||||
#define APM_ENT_TYPE_FREEBSD_NANDFS "FreeBSD-nandfs"
|
||||
#endif
|
||||
|
@ -211,7 +211,7 @@ gpt_mktbl(u_int tblsz)
|
||||
STAILQ_FOREACH(part, &partlist, link) {
|
||||
ent = tbl + part->index;
|
||||
gpt_uuid_enc(&ent->ent_type, ALIAS_TYPE2PTR(part->type));
|
||||
uuidgen(&uuid, 1);
|
||||
mkimg_uuid(&uuid);
|
||||
gpt_uuid_enc(&ent->ent_uuid, &uuid);
|
||||
le64enc(&ent->ent_lba_start, part->block);
|
||||
le64enc(&ent->ent_lba_end, part->block + part->size - 1);
|
||||
@ -279,7 +279,7 @@ gpt_write(lba_t imgsz, void *bootcode)
|
||||
le32enc(&hdr->hdr_size, offsetof(struct gpt_hdr, padding));
|
||||
le64enc(&hdr->hdr_lba_start, 2 + tblsz);
|
||||
le64enc(&hdr->hdr_lba_end, imgsz - tblsz - 2);
|
||||
uuidgen(&uuid, 1);
|
||||
mkimg_uuid(&uuid);
|
||||
gpt_uuid_enc(&hdr->hdr_uuid, &uuid);
|
||||
le32enc(&hdr->hdr_entries, nparts);
|
||||
le32enc(&hdr->hdr_entsz, sizeof(struct gpt_ent));
|
||||
|
@ -93,22 +93,50 @@ image_copyin(lba_t blk, int fd, uint64_t *sizep)
|
||||
|
||||
int
|
||||
image_copyout(int fd)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = image_copyout_region(fd, 0, image_size);
|
||||
if (!error)
|
||||
error = image_copyout_done(fd);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
image_copyout_done(int fd)
|
||||
{
|
||||
off_t ofs;
|
||||
int error;
|
||||
|
||||
ofs = lseek(fd, 0L, SEEK_CUR);
|
||||
if (ofs == -1)
|
||||
return (0);
|
||||
error = (ftruncate(fd, ofs) == -1) ? errno : 0;
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
image_copyout_region(int fd, lba_t blk, lba_t size)
|
||||
{
|
||||
char *buffer;
|
||||
off_t ofs;
|
||||
size_t sz;
|
||||
ssize_t rdsz, wrsz;
|
||||
int error;
|
||||
|
||||
ofs = lseek(fd, 0L, SEEK_CUR);
|
||||
|
||||
if (lseek(image_fd, 0, SEEK_SET) != 0)
|
||||
blk *= secsz;
|
||||
if (lseek(image_fd, blk, SEEK_SET) != blk)
|
||||
return (errno);
|
||||
buffer = malloc(BUFFER_SIZE);
|
||||
if (buffer == NULL)
|
||||
return (errno);
|
||||
error = 0;
|
||||
while (1) {
|
||||
rdsz = read(image_fd, buffer, BUFFER_SIZE);
|
||||
size *= secsz;
|
||||
while (size > 0) {
|
||||
sz = (BUFFER_SIZE < size) ? BUFFER_SIZE : size;
|
||||
rdsz = read(image_fd, buffer, sz);
|
||||
if (rdsz <= 0) {
|
||||
error = (rdsz < 0) ? errno : 0;
|
||||
break;
|
||||
@ -120,17 +148,40 @@ image_copyout(int fd)
|
||||
error = errno;
|
||||
break;
|
||||
}
|
||||
assert(wrsz == rdsz);
|
||||
size -= rdsz;
|
||||
}
|
||||
free(buffer);
|
||||
if (error)
|
||||
return (error);
|
||||
ofs = lseek(fd, 0L, SEEK_CUR);
|
||||
if (ofs == -1)
|
||||
return (errno);
|
||||
error = (ftruncate(fd, ofs) == -1) ? errno : 0;
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
image_data(lba_t blk, lba_t size)
|
||||
{
|
||||
char *buffer, *p;
|
||||
|
||||
blk *= secsz;
|
||||
if (lseek(image_fd, blk, SEEK_SET) != blk)
|
||||
return (1);
|
||||
|
||||
size *= secsz;
|
||||
buffer = malloc(size);
|
||||
if (buffer == NULL)
|
||||
return (1);
|
||||
|
||||
if (read(image_fd, buffer, size) != (ssize_t)size) {
|
||||
free(buffer);
|
||||
return (1);
|
||||
}
|
||||
|
||||
p = buffer;
|
||||
while (size > 0 && *p == '\0')
|
||||
size--, p++;
|
||||
|
||||
free(buffer);
|
||||
return ((size == 0) ? 0 : 1);
|
||||
}
|
||||
|
||||
lba_t
|
||||
image_get_size(void)
|
||||
{
|
||||
|
@ -33,6 +33,9 @@ typedef int64_t lba_t;
|
||||
|
||||
int image_copyin(lba_t blk, int fd, uint64_t *sizep);
|
||||
int image_copyout(int fd);
|
||||
int image_copyout_done(int fd);
|
||||
int image_copyout_region(int fd, lba_t blk, lba_t size);
|
||||
int image_data(lba_t blk, lba_t size);
|
||||
lba_t image_get_size(void);
|
||||
int image_init(void);
|
||||
int image_set_size(lba_t blk);
|
||||
|
@ -24,12 +24,12 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd July 2, 2014
|
||||
.Dd July 4, 2014
|
||||
.Dt MKIMG 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mkimg
|
||||
.Nd "utility to make a disk image"
|
||||
.Nd "utility to make disk images"
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl H Ar heads
|
||||
@ -40,6 +40,7 @@
|
||||
.Op Fl f Ar format
|
||||
.Op Fl o Ar outfile
|
||||
.Op Fl v
|
||||
.Op Fl y
|
||||
.Fl s Ar scheme
|
||||
.Fl p Ar partition
|
||||
.Op Fl p Ar partition ...
|
||||
@ -111,6 +112,16 @@ option increases the level of output that the
|
||||
.Nm
|
||||
utility prints.
|
||||
.Pp
|
||||
The
|
||||
.Op Fl y
|
||||
option is used for testing purposes only and is not to be used in production.
|
||||
When present, the
|
||||
.Nm
|
||||
utility will generate predictable values for Universally Unique Identifiers
|
||||
(UUIDs) and time stamps so that consecutive runs of the
|
||||
.Nm
|
||||
utility will create images that are identical.
|
||||
.Pp
|
||||
For a complete list of supported partitioning schemes or supported output
|
||||
format, or for a detailed description of how to specify partitions, run the
|
||||
.Nm
|
||||
|
@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/queue.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uuid.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
@ -50,6 +51,7 @@ __FBSDID("$FreeBSD$");
|
||||
struct partlisthead partlist = STAILQ_HEAD_INITIALIZER(partlist);
|
||||
u_int nparts = 0;
|
||||
|
||||
u_int unit_testing;
|
||||
u_int verbose;
|
||||
|
||||
u_int ncyls = 0;
|
||||
@ -73,6 +75,8 @@ usage(const char *why)
|
||||
fprintf(stderr, "\t-o <file>\t- file to write image into\n");
|
||||
fprintf(stderr, "\t-p <partition>\n");
|
||||
fprintf(stderr, "\t-s <scheme>\n");
|
||||
fprintf(stderr, "\t-v\t\t- increase verbosity\n");
|
||||
fprintf(stderr, "\t-y\t\t- [developers] enable unit test\n");
|
||||
fprintf(stderr, "\t-H <num>\t- number of heads to simulate\n");
|
||||
fprintf(stderr, "\t-P <num>\t- physical sector size\n");
|
||||
fprintf(stderr, "\t-S <num>\t- logical sector size\n");
|
||||
@ -258,6 +262,22 @@ sparse_write(int fd, const void *ptr, size_t sz)
|
||||
}
|
||||
#endif /* SPARSE_WRITE */
|
||||
|
||||
void
|
||||
mkimg_uuid(struct uuid *uuid)
|
||||
{
|
||||
static uint8_t gen[sizeof(struct uuid)];
|
||||
u_int i;
|
||||
|
||||
if (!unit_testing) {
|
||||
uuidgen(uuid, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(gen); i++)
|
||||
gen[i]++;
|
||||
memcpy(uuid, gen, sizeof(uuid_t));
|
||||
}
|
||||
|
||||
static void
|
||||
mkimg(void)
|
||||
{
|
||||
@ -337,7 +357,7 @@ main(int argc, char *argv[])
|
||||
|
||||
bcfd = -1;
|
||||
outfd = 1; /* Write to stdout by default */
|
||||
while ((c = getopt(argc, argv, "b:f:o:p:s:vH:P:S:T:")) != -1) {
|
||||
while ((c = getopt(argc, argv, "b:f:o:p:s:vyH:P:S:T:")) != -1) {
|
||||
switch (c) {
|
||||
case 'b': /* BOOT CODE */
|
||||
if (bcfd != -1)
|
||||
@ -373,6 +393,9 @@ main(int argc, char *argv[])
|
||||
if (error)
|
||||
errc(EX_DATAERR, error, "scheme");
|
||||
break;
|
||||
case 'y':
|
||||
unit_testing++;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
|
@ -50,6 +50,7 @@ struct part {
|
||||
extern STAILQ_HEAD(partlisthead, part) partlist;
|
||||
extern u_int nparts;
|
||||
|
||||
extern u_int unit_testing;
|
||||
extern u_int verbose;
|
||||
|
||||
extern u_int ncyls;
|
||||
@ -71,4 +72,7 @@ round_block(lba_t n)
|
||||
ssize_t sparse_write(int, const void *, size_t);
|
||||
#endif
|
||||
|
||||
struct uuid;
|
||||
void mkimg_uuid(struct uuid *);
|
||||
|
||||
#endif /* _MKIMG_MKIMG_H_ */
|
||||
|
@ -28,7 +28,6 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/apm.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/errno.h>
|
||||
#include <stdlib.h>
|
||||
|
385
usr.bin/mkimg/vhd.c
Normal file
385
usr.bin/mkimg/vhd.c
Normal file
@ -0,0 +1,385 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Marcel Moolenaar
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <uuid.h>
|
||||
|
||||
#include "image.h"
|
||||
#include "format.h"
|
||||
#include "mkimg.h"
|
||||
|
||||
#ifndef __has_extension
|
||||
#define __has_extension(x) 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* General notes:
|
||||
* o File is in network byte order.
|
||||
* o The timestamp is seconds since 1/1/2000 12:00:00 AM UTC
|
||||
*
|
||||
* This file is divided in 3 parts:
|
||||
* 1. Common definitions
|
||||
* 2. Dynamic VHD support
|
||||
* 3. Fixed VHD support
|
||||
*/
|
||||
|
||||
/*
|
||||
* PART 1: Common definitions
|
||||
*/
|
||||
|
||||
#define VHD_SECTOR_SIZE 512
|
||||
#define VHD_BLOCK_SIZE (4096 * VHD_SECTOR_SIZE) /* 2MB blocks */
|
||||
|
||||
struct vhd_footer {
|
||||
uint64_t cookie;
|
||||
#define VHD_FOOTER_COOKIE 0x636f6e6563746978
|
||||
uint32_t features;
|
||||
#define VHD_FEATURES_TEMPORARY 0x01
|
||||
#define VHD_FEATURES_RESERVED 0x02
|
||||
uint32_t version;
|
||||
#define VHD_VERSION 0x00010000
|
||||
uint64_t data_offset;
|
||||
uint32_t timestamp;
|
||||
uint32_t creator_tool;
|
||||
#define VHD_CREATOR_TOOL 0x2a696d67 /* FreeBSD mkimg */
|
||||
uint32_t creator_version;
|
||||
#define VHD_CREATOR_VERSION 0x00010000
|
||||
uint32_t creator_os;
|
||||
#define VHD_CREATOR_OS 0x46425344
|
||||
uint64_t original_size;
|
||||
uint64_t current_size;
|
||||
uint16_t cylinders;
|
||||
uint8_t heads;
|
||||
uint8_t sectors;
|
||||
uint32_t disk_type;
|
||||
#define VHD_DISK_TYPE_FIXED 2
|
||||
#define VHD_DISK_TYPE_DYNAMIC 3
|
||||
#define VHD_DISK_TYPE_DIFF 4
|
||||
uint32_t checksum;
|
||||
uuid_t id;
|
||||
uint8_t saved_state;
|
||||
uint8_t _reserved[427];
|
||||
};
|
||||
#if __has_extension(c_static_assert)
|
||||
_Static_assert(sizeof(struct vhd_footer) == VHD_SECTOR_SIZE,
|
||||
"Wrong size for footer");
|
||||
#endif
|
||||
|
||||
static uint32_t
|
||||
vhd_checksum(void *buf, size_t sz)
|
||||
{
|
||||
uint8_t *p = buf;
|
||||
uint32_t sum;
|
||||
size_t ofs;
|
||||
|
||||
sum = 0;
|
||||
for (ofs = 0; ofs < sz; ofs++)
|
||||
sum += p[ofs];
|
||||
return (~sum);
|
||||
}
|
||||
|
||||
static void
|
||||
vhd_geometry(struct vhd_footer *footer, uint64_t image_size)
|
||||
{
|
||||
lba_t imgsz;
|
||||
long cth;
|
||||
|
||||
/* Respect command line options if possible. */
|
||||
if (nheads > 1 && nheads < 256 &&
|
||||
nsecs > 1 && nsecs < 256 &&
|
||||
ncyls < 65536) {
|
||||
be16enc(&footer->cylinders, ncyls);
|
||||
footer->heads = nheads;
|
||||
footer->sectors = nsecs;
|
||||
return;
|
||||
}
|
||||
|
||||
imgsz = image_size / VHD_SECTOR_SIZE;
|
||||
if (imgsz > 65536 * 16 * 255)
|
||||
imgsz = 65536 * 16 * 255;
|
||||
if (imgsz >= 65535 * 16 * 63) {
|
||||
be16enc(&footer->cylinders, imgsz / (16 * 255));
|
||||
footer->heads = 16;
|
||||
footer->sectors = 255;
|
||||
return;
|
||||
}
|
||||
footer->sectors = 17;
|
||||
cth = imgsz / 17;
|
||||
footer->heads = (cth + 1023) / 1024;
|
||||
if (footer->heads < 4)
|
||||
footer->heads = 4;
|
||||
if (cth >= (footer->heads * 1024) || footer->heads > 16) {
|
||||
footer->heads = 16;
|
||||
footer->sectors = 31;
|
||||
cth = imgsz / 31;
|
||||
}
|
||||
if (cth >= (footer->heads * 1024)) {
|
||||
footer->heads = 16;
|
||||
footer->sectors = 63;
|
||||
cth = imgsz / 63;
|
||||
}
|
||||
be16enc(&footer->cylinders, cth / footer->heads);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
vhd_timestamp(void)
|
||||
{
|
||||
time_t t;
|
||||
|
||||
if (!unit_testing) {
|
||||
t = time(NULL);
|
||||
return (t - 0x386d4380);
|
||||
}
|
||||
|
||||
return (0x01234567);
|
||||
}
|
||||
|
||||
static void
|
||||
vhd_uuid_enc(void *buf, const uuid_t *uuid)
|
||||
{
|
||||
uint8_t *p = buf;
|
||||
int i;
|
||||
|
||||
be32enc(p, uuid->time_low);
|
||||
be16enc(p + 4, uuid->time_mid);
|
||||
be16enc(p + 6, uuid->time_hi_and_version);
|
||||
p[8] = uuid->clock_seq_hi_and_reserved;
|
||||
p[9] = uuid->clock_seq_low;
|
||||
for (i = 0; i < _UUID_NODE_LEN; i++)
|
||||
p[10 + i] = uuid->node[i];
|
||||
}
|
||||
|
||||
static void
|
||||
vhd_make_footer(struct vhd_footer *footer, uint64_t image_size,
|
||||
uint32_t disk_type, uint64_t data_offset)
|
||||
{
|
||||
uuid_t id;
|
||||
|
||||
memset(footer, 0, sizeof(*footer));
|
||||
be64enc(&footer->cookie, VHD_FOOTER_COOKIE);
|
||||
be32enc(&footer->features, VHD_FEATURES_RESERVED);
|
||||
be32enc(&footer->version, VHD_VERSION);
|
||||
be64enc(&footer->data_offset, data_offset);
|
||||
be32enc(&footer->timestamp, vhd_timestamp());
|
||||
be32enc(&footer->creator_tool, VHD_CREATOR_TOOL);
|
||||
be32enc(&footer->creator_version, VHD_CREATOR_VERSION);
|
||||
be32enc(&footer->creator_os, VHD_CREATOR_OS);
|
||||
be64enc(&footer->original_size, image_size);
|
||||
be64enc(&footer->current_size, image_size);
|
||||
vhd_geometry(footer, image_size);
|
||||
be32enc(&footer->disk_type, disk_type);
|
||||
mkimg_uuid(&id);
|
||||
vhd_uuid_enc(&footer->id, &id);
|
||||
be32enc(&footer->checksum, vhd_checksum(footer, sizeof(*footer)));
|
||||
}
|
||||
|
||||
/*
|
||||
* We round the image size to 2MB for both the dynamic and
|
||||
* fixed VHD formats. For dynamic VHD, this is needed to
|
||||
* have the image size be a multiple of the grain size. For
|
||||
* fixed VHD this is not really needed, but makes sure that
|
||||
* it's easy to convert from fixed VHD to dynamic VHD.
|
||||
*/
|
||||
static int
|
||||
vhd_resize(lba_t imgsz)
|
||||
{
|
||||
uint64_t imagesz;
|
||||
|
||||
imagesz = imgsz * secsz;
|
||||
imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1);
|
||||
return (image_set_size(imagesz / secsz));
|
||||
}
|
||||
|
||||
/*
|
||||
* PART 2: Dynamic VHD support
|
||||
*
|
||||
* Notes:
|
||||
* o File layout:
|
||||
* copy of disk footer
|
||||
* dynamic disk header
|
||||
* block allocation table (BAT)
|
||||
* data blocks
|
||||
* disk footer
|
||||
*/
|
||||
|
||||
struct vhd_dyn_header {
|
||||
uint64_t cookie;
|
||||
#define VHD_HEADER_COOKIE 0x6378737061727365
|
||||
uint64_t data_offset;
|
||||
uint64_t table_offset;
|
||||
uint32_t version;
|
||||
uint32_t max_entries;
|
||||
uint32_t block_size;
|
||||
uint32_t checksum;
|
||||
uuid_t parent_id;
|
||||
uint32_t parent_timestamp;
|
||||
char _reserved1[4];
|
||||
uint16_t parent_name[256]; /* UTF-16 */
|
||||
struct {
|
||||
uint32_t code;
|
||||
uint32_t data_space;
|
||||
uint32_t data_length;
|
||||
uint32_t _reserved;
|
||||
uint64_t data_offset;
|
||||
} parent_locator[8];
|
||||
char _reserved2[256];
|
||||
};
|
||||
#if __has_extension(c_static_assert)
|
||||
_Static_assert(sizeof(struct vhd_dyn_header) == VHD_SECTOR_SIZE * 2,
|
||||
"Wrong size for header");
|
||||
#endif
|
||||
|
||||
static int
|
||||
vhd_dyn_write(int fd)
|
||||
{
|
||||
struct vhd_footer footer;
|
||||
struct vhd_dyn_header header;
|
||||
uint64_t imgsz;
|
||||
lba_t blk, blkcnt, nblks;
|
||||
uint32_t *bat;
|
||||
void *bitmap;
|
||||
size_t batsz;
|
||||
uint32_t sector;
|
||||
int bat_entries, error, entry;
|
||||
|
||||
imgsz = image_get_size() * secsz;
|
||||
bat_entries = imgsz / VHD_BLOCK_SIZE;
|
||||
|
||||
vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_DYNAMIC, sizeof(footer));
|
||||
if (sparse_write(fd, &footer, sizeof(footer)) < 0)
|
||||
return (errno);
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
be64enc(&header.cookie, VHD_HEADER_COOKIE);
|
||||
be64enc(&header.data_offset, ~0ULL);
|
||||
be64enc(&header.table_offset, sizeof(footer) + sizeof(header));
|
||||
be32enc(&header.version, VHD_VERSION);
|
||||
be32enc(&header.max_entries, bat_entries);
|
||||
be32enc(&header.block_size, VHD_BLOCK_SIZE);
|
||||
be32enc(&header.checksum, vhd_checksum(&header, sizeof(header)));
|
||||
if (sparse_write(fd, &header, sizeof(header)) < 0)
|
||||
return (errno);
|
||||
|
||||
batsz = bat_entries * sizeof(uint32_t);
|
||||
batsz = (batsz + VHD_SECTOR_SIZE - 1) & ~(VHD_SECTOR_SIZE - 1);
|
||||
bat = malloc(batsz);
|
||||
if (bat == NULL)
|
||||
return (errno);
|
||||
memset(bat, 0xff, batsz);
|
||||
blkcnt = VHD_BLOCK_SIZE / secsz;
|
||||
sector = (sizeof(footer) + sizeof(header) + batsz) / VHD_SECTOR_SIZE;
|
||||
for (entry = 0; entry < bat_entries; entry++) {
|
||||
blk = entry * blkcnt;
|
||||
if (image_data(blk, blkcnt)) {
|
||||
be32enc(&bat[entry], sector);
|
||||
sector += (VHD_BLOCK_SIZE / VHD_SECTOR_SIZE) + 1;
|
||||
}
|
||||
}
|
||||
if (sparse_write(fd, bat, batsz) < 0) {
|
||||
free(bat);
|
||||
return (errno);
|
||||
}
|
||||
free(bat);
|
||||
|
||||
bitmap = malloc(VHD_SECTOR_SIZE);
|
||||
if (bitmap == NULL)
|
||||
return (errno);
|
||||
memset(bitmap, 0xff, VHD_SECTOR_SIZE);
|
||||
|
||||
blk = 0;
|
||||
blkcnt = VHD_BLOCK_SIZE / secsz;
|
||||
nblks = image_get_size();
|
||||
while (blk < nblks) {
|
||||
if (!image_data(blk, blkcnt)) {
|
||||
blk += blkcnt;
|
||||
continue;
|
||||
}
|
||||
if (sparse_write(fd, bitmap, VHD_SECTOR_SIZE) < 0) {
|
||||
error = errno;
|
||||
break;
|
||||
}
|
||||
error = image_copyout_region(fd, blk, blkcnt);
|
||||
if (error)
|
||||
break;
|
||||
blk += blkcnt;
|
||||
}
|
||||
free(bitmap);
|
||||
if (blk != nblks)
|
||||
return (error);
|
||||
|
||||
if (sparse_write(fd, &footer, sizeof(footer)) < 0)
|
||||
return (errno);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct mkimg_format vhd_dyn_format = {
|
||||
.name = "vhd",
|
||||
.description = "Virtual Hard Disk",
|
||||
.resize = vhd_resize,
|
||||
.write = vhd_dyn_write,
|
||||
};
|
||||
|
||||
FORMAT_DEFINE(vhd_dyn_format);
|
||||
|
||||
/*
|
||||
* PART 2: Fixed VHD
|
||||
*/
|
||||
|
||||
static int
|
||||
vhd_fix_write(int fd)
|
||||
{
|
||||
struct vhd_footer footer;
|
||||
uint64_t imgsz;
|
||||
int error;
|
||||
|
||||
error = image_copyout(fd);
|
||||
if (!error) {
|
||||
imgsz = image_get_size() * secsz;
|
||||
vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_FIXED, ~0ULL);
|
||||
if (sparse_write(fd, &footer, sizeof(footer)) < 0)
|
||||
error = errno;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static struct mkimg_format vhd_fix_format = {
|
||||
.name = "vhdf",
|
||||
.description = "Fixed Virtual Hard Disk",
|
||||
.resize = vhd_resize,
|
||||
.write = vhd_fix_write,
|
||||
};
|
||||
|
||||
FORMAT_DEFINE(vhd_fix_format);
|
@ -28,7 +28,6 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/apm.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/errno.h>
|
||||
#include <stdint.h>
|
||||
@ -115,8 +114,9 @@ vmdk_write(int fd)
|
||||
char *buf, *desc;
|
||||
off_t cur, lim;
|
||||
uint64_t imagesz;
|
||||
lba_t blkofs, blkcnt;
|
||||
size_t gdsz, gtsz;
|
||||
uint32_t sec;
|
||||
uint32_t sec, cursec;
|
||||
int error, desc_len, n, ngrains, ngts;
|
||||
|
||||
imagesz = (image_get_size() * secsz) / VMDK_SECTOR_SIZE;
|
||||
@ -179,8 +179,15 @@ vmdk_write(int fd)
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
for (n = 0; n < ngrains; n++)
|
||||
le32enc(gt + n, sec + n * grainsz);
|
||||
cursec = sec;
|
||||
blkcnt = (grainsz * VMDK_SECTOR_SIZE) / secsz;
|
||||
for (n = 0; n < ngrains; n++) {
|
||||
blkofs = n * blkcnt;
|
||||
if (image_data(blkofs, blkcnt)) {
|
||||
le32enc(gt + n, cursec);
|
||||
cursec += grainsz;
|
||||
}
|
||||
}
|
||||
|
||||
error = 0;
|
||||
if (!error && sparse_write(fd, &hdr, VMDK_SECTOR_SIZE) < 0)
|
||||
@ -211,9 +218,19 @@ vmdk_write(int fd)
|
||||
if (buf != NULL)
|
||||
free(buf);
|
||||
}
|
||||
if (!error)
|
||||
error = image_copyout(fd);
|
||||
return (error);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
blkcnt = (grainsz * VMDK_SECTOR_SIZE) / secsz;
|
||||
for (n = 0; n < ngrains; n++) {
|
||||
blkofs = n * blkcnt;
|
||||
if (image_data(blkofs, blkcnt)) {
|
||||
error = image_copyout_region(fd, blkofs, blkcnt);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
return (image_copyout_done(fd));
|
||||
}
|
||||
|
||||
static struct mkimg_format vmdk_format = {
|
||||
|
Loading…
Reference in New Issue
Block a user