diff options
-rw-r--r-- | usr.bin/mkimg/gpt.c | 2 | ||||
-rw-r--r-- | usr.bin/mkimg/image.c | 12 | ||||
-rw-r--r-- | usr.bin/mkimg/image.h | 1 | ||||
-rw-r--r-- | usr.bin/mkimg/mbr.c | 1 | ||||
-rw-r--r-- | usr.bin/mkimg/mkimg.1 | 87 | ||||
-rw-r--r-- | usr.bin/mkimg/scheme.c | 1 | ||||
-rw-r--r-- | usr.bin/mkimg/scheme.h | 1 | ||||
-rw-r--r-- | usr.bin/mkimg/vhd.c | 92 |
8 files changed, 151 insertions, 46 deletions
diff --git a/usr.bin/mkimg/gpt.c b/usr.bin/mkimg/gpt.c index 5773a6a..678e636 100644 --- a/usr.bin/mkimg/gpt.c +++ b/usr.bin/mkimg/gpt.c @@ -57,6 +57,7 @@ static uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; static uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; static uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; static uuid_t gpt_uuid_mbr = GPT_ENT_TYPE_MBR; +static uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; static struct mkimg_alias gpt_aliases[] = { { ALIAS_EFI, ALIAS_PTR2TYPE(&gpt_uuid_efi) }, @@ -68,6 +69,7 @@ static struct mkimg_alias gpt_aliases[] = { { ALIAS_FREEBSD_VINUM, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_vinum) }, { ALIAS_FREEBSD_ZFS, ALIAS_PTR2TYPE(&gpt_uuid_freebsd_zfs) }, { ALIAS_MBR, ALIAS_PTR2TYPE(&gpt_uuid_mbr) }, + { ALIAS_NTFS, ALIAS_PTR2TYPE(&gpt_uuid_ms_basic_data) }, { ALIAS_NONE, 0 } /* Keep last! */ }; diff --git a/usr.bin/mkimg/image.c b/usr.bin/mkimg/image.c index be1c2e9..a3bec63 100644 --- a/usr.bin/mkimg/image.c +++ b/usr.bin/mkimg/image.c @@ -517,14 +517,14 @@ image_copyout_memory(int fd, size_t size, void *ptr) return (0); } -static int -image_copyout_zeroes(int fd, size_t size) +int +image_copyout_zeroes(int fd, size_t count) { static uint8_t *zeroes = NULL; size_t sz; int error; - if (lseek(fd, (off_t)size, SEEK_CUR) != -1) + if (lseek(fd, (off_t)count, SEEK_CUR) != -1) return (0); /* @@ -537,12 +537,12 @@ image_copyout_zeroes(int fd, size_t size) return (ENOMEM); } - while (size > 0) { - sz = (size > secsz) ? secsz : size; + while (count > 0) { + sz = (count > secsz) ? secsz : count; error = image_copyout_memory(fd, sz, zeroes); if (error) return (error); - size -= sz; + count -= sz; } return (0); } diff --git a/usr.bin/mkimg/image.h b/usr.bin/mkimg/image.h index ce195d9..0405c5b 100644 --- a/usr.bin/mkimg/image.h +++ b/usr.bin/mkimg/image.h @@ -35,6 +35,7 @@ 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_copyout_zeroes(int fd, size_t count); int image_data(lba_t blk, lba_t size); lba_t image_get_size(void); int image_init(void); diff --git a/usr.bin/mkimg/mbr.c b/usr.bin/mkimg/mbr.c index 9d737a5..961ca45 100644 --- a/usr.bin/mkimg/mbr.c +++ b/usr.bin/mkimg/mbr.c @@ -51,6 +51,7 @@ static struct mkimg_alias mbr_aliases[] = { { ALIAS_EFI, ALIAS_INT2TYPE(DOSPTYP_EFI) }, { ALIAS_FAT32, ALIAS_INT2TYPE(DOSPTYP_FAT32) }, { ALIAS_FREEBSD, ALIAS_INT2TYPE(DOSPTYP_386BSD) }, + { ALIAS_NTFS, ALIAS_INT2TYPE(DOSPTYP_NTFS) }, { ALIAS_NONE, 0 } /* Keep last! */ }; diff --git a/usr.bin/mkimg/mkimg.1 b/usr.bin/mkimg/mkimg.1 index 3b1d63e..1fecdd2 100644 --- a/usr.bin/mkimg/mkimg.1 +++ b/usr.bin/mkimg/mkimg.1 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 22, 2015 +.Dd August 7, 2015 .Dt MKIMG 1 .Os .Sh NAME @@ -141,7 +141,7 @@ utility will create images that are identical. .Pp A set of long options exist to query about the .Nm -utilty itself. +utility itself. Options in this set should be given by themselves because the .Nm utility exits immediately after providing the requested information. @@ -165,6 +165,85 @@ run the .Nm utility without any arguments. This will print a usage message with all the necessary details. +.Sh DISK FORMATS +The +.Nm +utility supports a number of output file formats. +A short description of these is given below. +.Ss QCOW and QCOW2 +QCOW stands for "QEMU Copy On Write". +It's a sparse file format akin to VHD and VMDK and QCOW represents the +first version. +QCOW2 represents version 2 of the file format. +Version 2 is not backward compatible with version 1 and adds support for +snapshots among other things. +The QCOW file formats are natively supported by QEMU and Xen. +To write QCOW, specify +.Fl f Ar qcow +on the command line. +To write version 2 QCOW, specify +.Fl f Ar qcow2 +on the command line. +The preferred file extension is ".qcow" and ".qcow2" for QCOW and QCOW2 +(resp.), but ".qcow" is sometimes used for version 2 files as well. +.Ss RAW file format +This file format is a sector by sector representation of an actual disk. +There is no extra information that describes or relates to the format +itself. The size of the file is the size of the (virtual) disk. +This file format is suitable for being copyied onto a disk with utilities +like +.Nm dd . +To write a raw disk file, either omit the +.Fl f +option, or specify +.Fl f Ar raw +on the command line. +The preferred file extension is one of ".img" or ".raw", but there's no +real convention for it. +.Ss Dynamic VHD and Fixed VHD +Microsoft's "Virtual Hard Disk" file formats. +The dynamic format is a sparse format akin to QCOW and VMDK. +The fixed format is effectively a raw format with a footer appended to the +file and as such it's often indistinguishable from the raw format. +The fixed file format has been added to support Microsoft's Azure platform +and due to inconsistencies in interpretation of the footer is not compatible +with utilities like +.Nm qemu +when it is specifically instructed to interpreted the file as a VHD file. +By default +.Nm qemu +will treat the file as a raw disk file, which mostly works fine. +To have +.Nm +create a dynamic VHD file, specify +.Fl f Ar vhd +on the command line. +To create a fixed VHD file for use by Azure, specify +.Fl f Ar vhdf +on the command line. +The preferred file extension is ".vhd". +.Ss VMDK +VMware's "Virtual Machine Disk" file format. +It's a sparse file format akin to QCOW and VHD and supported by many +virtualization solutions. +To create a VMDK file, specify +.Fl f Ar vmdk +on the command line. +The preferred file extension is ".vmdk". +.Pp +Not all virtualization solutions support all file formats, but often those +virtualization environments have utilities to convert from one format to +another. +Note however that conversion may require that the virtual disk size is +changed to match the constraints of the output format and this may invalidate +the contents of the disk image. +For example, the GUID Partition Table (GPT) scheme has a header in the last +sector on the disk. +When changing the disk size, the GPT must be changed so that the last header +is moved accordingly. +This is typically not part of the conversion process. +If possible, use an output format specifically for the environment in which +the file is intended to be used. .Sh ENVIRONMENT .Bl -tag -width "TMPDIR" -compact .It Ev TMPDIR @@ -235,6 +314,7 @@ utility supports assigning labels to the partitions specified. In the following example the file system partition is labeled as 'backup': .Dl % mkimg -s gpt -p freebsd-ufs/backup:=file-system.ufs -o gpt.img .Sh SEE ALSO +.Xr dd 1 , .Xr gpart 8 , .Xr makefs 8 , .Xr mdconfig 8 , @@ -247,4 +327,5 @@ utility first appeared in .Sh AUTHORS The .Nm -utility and manpage were written by Marcel Moolenaar <marcelm@juniper.net> +utility and manpage were written by +.An Marcel Moolenaar Aq Mt marcelm@juniper.net . diff --git a/usr.bin/mkimg/scheme.c b/usr.bin/mkimg/scheme.c index 336f953..9bdf8a5 100644 --- a/usr.bin/mkimg/scheme.c +++ b/usr.bin/mkimg/scheme.c @@ -59,6 +59,7 @@ static struct { { "freebsd-vinum", ALIAS_FREEBSD_VINUM }, { "freebsd-zfs", ALIAS_FREEBSD_ZFS }, { "mbr", ALIAS_MBR }, + { "ntfs", ALIAS_NTFS }, { NULL, ALIAS_NONE } /* Keep last! */ }; diff --git a/usr.bin/mkimg/scheme.h b/usr.bin/mkimg/scheme.h index d594a19..73b06eb 100644 --- a/usr.bin/mkimg/scheme.h +++ b/usr.bin/mkimg/scheme.h @@ -45,6 +45,7 @@ enum alias { ALIAS_FREEBSD_VINUM, ALIAS_FREEBSD_ZFS, ALIAS_MBR, + ALIAS_NTFS, /* end */ ALIAS_COUNT /* Keep last! */ }; diff --git a/usr.bin/mkimg/vhd.c b/usr.bin/mkimg/vhd.c index eb93fac..c7d1f84 100644 --- a/usr.bin/mkimg/vhd.c +++ b/usr.bin/mkimg/vhd.c @@ -159,6 +159,34 @@ vhd_geometry(uint64_t image_size, struct vhd_geom *geom) geom->cylinders = cth / geom->heads; } +static uint64_t +vhd_resize(uint64_t origsz) +{ + struct vhd_geom geom; + uint64_t newsz; + + /* + * Round the image size to the pre-determined geometry that + * matches the image size. This circular dependency implies + * that we need to loop to handle boundary conditions. + * The first time, newsz equals origsz and the geometry will + * typically yield a new size that's smaller. We keep adding + * cylinder's worth of sectors to the new size until its + * larger or equal or origsz. But during those iterations, + * the geometry can change, so we need to account for that. + */ + newsz = origsz; + while (1) { + vhd_geometry(newsz, &geom); + newsz = (int64_t)geom.cylinders * geom.heads * + geom.sectors * VHD_SECTOR_SIZE; + if (newsz >= origsz) + break; + newsz += geom.heads * geom.sectors * VHD_SECTOR_SIZE; + } + return (newsz); +} + static uint32_t vhd_timestamp(void) { @@ -256,8 +284,7 @@ vhd_dyn_resize(lba_t imgsz) { uint64_t imagesz; - imagesz = imgsz * secsz; - imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1); + imagesz = vhd_resize(imgsz * secsz); return (image_set_size(imagesz / secsz)); } @@ -266,7 +293,7 @@ vhd_dyn_write(int fd) { struct vhd_footer footer; struct vhd_dyn_header header; - uint64_t imgsz; + uint64_t imgsz, rawsz; lba_t blk, blkcnt, nblks; uint32_t *bat; void *bitmap; @@ -274,13 +301,14 @@ vhd_dyn_write(int fd) uint32_t sector; int bat_entries, error, entry; - imgsz = image_get_size() * secsz; - bat_entries = imgsz / VHD_BLOCK_SIZE; + rawsz = image_get_size() * secsz; + imgsz = (rawsz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1); - vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_DYNAMIC, sizeof(footer)); + vhd_make_footer(&footer, rawsz, VHD_DISK_TYPE_DYNAMIC, sizeof(footer)); if (sparse_write(fd, &footer, sizeof(footer)) < 0) return (errno); + bat_entries = imgsz / VHD_BLOCK_SIZE; memset(&header, 0, sizeof(header)); be64enc(&header.cookie, VHD_HEADER_COOKIE); be64enc(&header.data_offset, ~0ULL); @@ -321,7 +349,7 @@ vhd_dyn_write(int fd) blk = 0; blkcnt = VHD_BLOCK_SIZE / secsz; error = 0; - nblks = image_get_size(); + nblks = rawsz / secsz; while (blk < nblks) { if (!image_data(blk, blkcnt)) { blk += blkcnt; @@ -331,15 +359,20 @@ vhd_dyn_write(int fd) error = errno; break; } + /* Handle partial last block */ + if (blk + blkcnt > nblks) + blkcnt = nblks - blk; error = image_copyout_region(fd, blk, blkcnt); if (error) break; blk += blkcnt; } free(bitmap); - if (blk != nblks) + if (error) + return (error); + error = image_copyout_zeroes(fd, imgsz - rawsz); + if (error) return (error); - if (sparse_write(fd, &footer, sizeof(footer)) < 0) return (errno); @@ -362,24 +395,9 @@ FORMAT_DEFINE(vhd_dyn_format); static int vhd_fix_resize(lba_t imgsz) { - struct vhd_geom geom; - int64_t imagesz; + uint64_t imagesz; - /* - * Round the image size to the pre-determined geometry that - * matches the image size. This circular dependency implies - * that we need to loop to handle boundary conditions. - */ - imgsz *= secsz; - imagesz = imgsz; - while (1) { - vhd_geometry(imagesz, &geom); - imagesz = (int64_t)geom.cylinders * geom.heads * - geom.sectors * VHD_SECTOR_SIZE; - if (imagesz >= imgsz) - break; - imagesz += geom.heads * geom.sectors * VHD_SECTOR_SIZE; - } + imagesz = vhd_resize(imgsz * secsz); /* * Azure demands that images are a whole number of megabytes. */ @@ -391,24 +409,24 @@ static int vhd_fix_write(int fd) { struct vhd_footer footer; - uint64_t imgsz; + uint64_t imagesz; 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; - } + if (error) + return (error); + + imagesz = image_get_size() * secsz; + vhd_make_footer(&footer, imagesz, VHD_DISK_TYPE_FIXED, ~0ULL); + error = (sparse_write(fd, &footer, sizeof(footer)) < 0) ? errno : 0; return (error); } static struct mkimg_format vhd_fix_format = { - .name = "vhdf", - .description = "Fixed Virtual Hard Disk", - .resize = vhd_fix_resize, - .write = vhd_fix_write, + .name = "vhdf", + .description = "Fixed Virtual Hard Disk", + .resize = vhd_fix_resize, + .write = vhd_fix_write, }; FORMAT_DEFINE(vhd_fix_format); |