1227825Stheraven/*- 2227825Stheraven * Copyright (c) 2014 Juniper Networks, Inc. 3227825Stheraven * All rights reserved. 4227825Stheraven * 5227825Stheraven * Redistribution and use in source and binary forms, with or without 6227825Stheraven * modification, are permitted provided that the following conditions 7227825Stheraven * are met: 8227825Stheraven * 1. Redistributions of source code must retain the above copyright 9227825Stheraven * notice, this list of conditions and the following disclaimer. 10227825Stheraven * 2. Redistributions in binary form must reproduce the above copyright 11227825Stheraven * notice, this list of conditions and the following disclaimer in the 12227825Stheraven * documentation and/or other materials provided with the distribution. 13227825Stheraven * 14227825Stheraven * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15227825Stheraven * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16227825Stheraven * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17227825Stheraven * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18227825Stheraven * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19227825Stheraven * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20227825Stheraven * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21227825Stheraven * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22227825Stheraven * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23227825Stheraven * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24227825Stheraven * SUCH DAMAGE. 25227825Stheraven */ 26227825Stheraven 27227825Stheraven#include <sys/cdefs.h> 28227825Stheraven#include <sys/errno.h> 29227825Stheraven#include <stdint.h> 30227825Stheraven#include <stdio.h> 31227825Stheraven#include <stdlib.h> 32227825Stheraven#include <string.h> 33227825Stheraven 34227825Stheraven#include "endian.h" 35227825Stheraven#include "image.h" 36227825Stheraven#include "format.h" 37227825Stheraven#include "mkimg.h" 38227825Stheraven 39227825Stheraven#define VMDK_IMAGE_ROUND 1048576 40227825Stheraven#define VMDK_MIN_GRAIN_SIZE 8192 41227825Stheraven#define VMDK_SECTOR_SIZE 512 42227825Stheraven 43227825Stheravenstruct vmdk_header { 44227825Stheraven uint32_t magic; 45227825Stheraven#define VMDK_MAGIC 0x564d444b 46227825Stheraven uint32_t version; 47227825Stheraven#define VMDK_VERSION 1 48227825Stheraven uint32_t flags; 49227825Stheraven#define VMDK_FLAGS_NL_TEST (1 << 0) 50227825Stheraven#define VMDK_FLAGS_RGT_USED (1 << 1) 51227825Stheraven#define VMDK_FLAGS_COMPRESSED (1 << 16) 52227825Stheraven#define VMDK_FLAGS_MARKERS (1 << 17) 53227825Stheraven uint64_t capacity; 54227825Stheraven uint64_t grain_size; 55227825Stheraven uint64_t desc_offset; 56227825Stheraven uint64_t desc_size; 57227825Stheraven uint32_t ngtes; 58227825Stheraven#define VMDK_NGTES 512 59227825Stheraven uint64_t rgd_offset; 60227825Stheraven uint64_t gd_offset; 61227825Stheraven uint64_t overhead; 62227825Stheraven uint8_t unclean; 63227825Stheraven uint32_t nl_test; 64227825Stheraven#define VMDK_NL_TEST 0x0a200d0a 65227825Stheraven uint16_t compress; 66227825Stheraven#define VMDK_COMPRESS_NONE 0 67227825Stheraven#define VMDK_COMPRESS_DEFLATE 1 68227825Stheraven char padding[433]; 69227825Stheraven} __attribute__((__packed__)); 70227825Stheraven 71227825Stheravenstatic const char desc_fmt[] = 72227825Stheraven "# Disk DescriptorFile\n" 73227825Stheraven "version=%d\n" 74227825Stheraven "CID=%08x\n" 75227825Stheraven "parentCID=ffffffff\n" 76227825Stheraven "createType=\"monolithicSparse\"\n" 77227825Stheraven "# Extent description\n" 78227825Stheraven "RW %ju SPARSE \"%s\"\n" 79227825Stheraven "# The Disk Data Base\n" 80227825Stheraven "#DDB\n" 81227825Stheraven "ddb.adapterType = \"ide\"\n" 82227825Stheraven "ddb.geometry.cylinders = \"%u\"\n" 83227825Stheraven "ddb.geometry.heads = \"%u\"\n" 84227825Stheraven "ddb.geometry.sectors = \"%u\"\n"; 85227825Stheraven 86227825Stheravenstatic uint64_t grainsz; 87227825Stheraven 88227825Stheravenstatic int 89227825Stheravenvmdk_resize(lba_t imgsz) 90227825Stheraven{ 91227825Stheraven uint64_t imagesz; 92227825Stheraven 93227825Stheraven imagesz = imgsz * secsz; 94227825Stheraven imagesz = (imagesz + VMDK_IMAGE_ROUND - 1) & ~(VMDK_IMAGE_ROUND - 1); 95227825Stheraven grainsz = (blksz < VMDK_MIN_GRAIN_SIZE) ? VMDK_MIN_GRAIN_SIZE : blksz; 96227825Stheraven 97227825Stheraven if (verbose) 98227825Stheraven fprintf(stderr, "VMDK: image size = %ju, grain size = %ju\n", 99227825Stheraven (uintmax_t)imagesz, (uintmax_t)grainsz); 100227825Stheraven 101227825Stheraven grainsz /= VMDK_SECTOR_SIZE; 102227825Stheraven return (image_set_size(imagesz / secsz)); 103227825Stheraven} 104227825Stheraven 105227825Stheravenstatic int 106227825Stheravenvmdk_write(int fd) 107227825Stheraven{ 108227825Stheraven struct vmdk_header hdr; 109227825Stheraven uint32_t *gt, *gd, *rgd; 110227825Stheraven char *buf, *desc; 111227825Stheraven off_t cur, lim; 112227825Stheraven uint64_t imagesz; 113227825Stheraven lba_t blkofs, blkcnt; 114227825Stheraven size_t gdsz, gtsz; 115227825Stheraven uint32_t sec, cursec; 116227825Stheraven int error, desc_len, n, ngrains, ngts; 117227825Stheraven 118227825Stheraven imagesz = (image_get_size() * secsz) / VMDK_SECTOR_SIZE; 119227825Stheraven 120227825Stheraven memset(&hdr, 0, sizeof(hdr)); 121227825Stheraven le32enc(&hdr.magic, VMDK_MAGIC); 122227825Stheraven le32enc(&hdr.version, VMDK_VERSION); 123227825Stheraven le32enc(&hdr.flags, VMDK_FLAGS_NL_TEST | VMDK_FLAGS_RGT_USED); 124227825Stheraven le64enc(&hdr.capacity, imagesz); 125227825Stheraven le64enc(&hdr.grain_size, grainsz); 126227825Stheraven 127227825Stheraven n = asprintf(&desc, desc_fmt, 1 /*version*/, 0 /*CID*/, 128227825Stheraven (uintmax_t)imagesz /*size*/, "" /*name*/, 129227825Stheraven ncyls /*cylinders*/, nheads /*heads*/, nsecs /*sectors*/); 130227825Stheraven if (n == -1) 131227825Stheraven return (ENOMEM); 132227825Stheraven 133227825Stheraven desc_len = (n + VMDK_SECTOR_SIZE - 1) & ~(VMDK_SECTOR_SIZE - 1); 134227825Stheraven desc = realloc(desc, desc_len); 135227825Stheraven memset(desc + n, 0, desc_len - n); 136227825Stheraven 137227825Stheraven le64enc(&hdr.desc_offset, 1); 138227825Stheraven le64enc(&hdr.desc_size, desc_len / VMDK_SECTOR_SIZE); 139227825Stheraven le32enc(&hdr.ngtes, VMDK_NGTES); 140227825Stheraven 141227825Stheraven sec = desc_len / VMDK_SECTOR_SIZE + 1; 142227825Stheraven 143227825Stheraven ngrains = imagesz / grainsz; 144227825Stheraven ngts = (ngrains + VMDK_NGTES - 1) / VMDK_NGTES; 145227825Stheraven gdsz = (ngts * sizeof(uint32_t) + VMDK_SECTOR_SIZE - 1) & 146227825Stheraven ~(VMDK_SECTOR_SIZE - 1); 147227825Stheraven 148227825Stheraven gd = calloc(1, gdsz); 149227825Stheraven if (gd == NULL) { 150227825Stheraven free(desc); 151227825Stheraven return (ENOMEM); 152227825Stheraven } 153227825Stheraven le64enc(&hdr.gd_offset, sec); 154227825Stheraven sec += gdsz / VMDK_SECTOR_SIZE; 155227825Stheraven for (n = 0; n < ngts; n++) { 156227825Stheraven le32enc(gd + n, sec); 157227825Stheraven sec += VMDK_NGTES * sizeof(uint32_t) / VMDK_SECTOR_SIZE; 158227825Stheraven } 159227825Stheraven 160227825Stheraven rgd = calloc(1, gdsz); 161227825Stheraven if (rgd == NULL) { 162227825Stheraven free(gd); 163227825Stheraven free(desc); 164227825Stheraven return (ENOMEM); 165227825Stheraven } 166227825Stheraven le64enc(&hdr.rgd_offset, sec); 167227825Stheraven sec += gdsz / VMDK_SECTOR_SIZE; 168227825Stheraven for (n = 0; n < ngts; n++) { 169227825Stheraven le32enc(rgd + n, sec); 170227825Stheraven sec += VMDK_NGTES * sizeof(uint32_t) / VMDK_SECTOR_SIZE; 171227825Stheraven } 172227825Stheraven 173227825Stheraven sec = (sec + grainsz - 1) & ~(grainsz - 1); 174227825Stheraven 175227825Stheraven if (verbose) 176227825Stheraven fprintf(stderr, "VMDK: overhead = %ju\n", 177227825Stheraven (uintmax_t)(sec * VMDK_SECTOR_SIZE)); 178227825Stheraven 179227825Stheraven le64enc(&hdr.overhead, sec); 180227825Stheraven be32enc(&hdr.nl_test, VMDK_NL_TEST); 181227825Stheraven 182227825Stheraven gt = calloc(ngts, VMDK_NGTES * sizeof(uint32_t)); 183227825Stheraven if (gt == NULL) { 184249998Sdim free(rgd); 185227825Stheraven free(gd); 186227825Stheraven free(desc); 187227825Stheraven return (ENOMEM); 188227825Stheraven } 189249998Sdim gtsz = ngts * VMDK_NGTES * sizeof(uint32_t); 190227825Stheraven 191227825Stheraven cursec = sec; 192227825Stheraven blkcnt = (grainsz * VMDK_SECTOR_SIZE) / secsz; 193227825Stheraven for (n = 0; n < ngrains; n++) { 194227825Stheraven blkofs = n * blkcnt; 195249998Sdim if (image_data(blkofs, blkcnt)) { 196243673Stheraven le32enc(gt + n, cursec); 197243673Stheraven cursec += grainsz; 198243673Stheraven } 199232950Stheraven } 200232950Stheraven 201227825Stheraven error = 0; 202227825Stheraven if (!error && sparse_write(fd, &hdr, VMDK_SECTOR_SIZE) < 0) 203227825Stheraven error = errno; 204227825Stheraven if (!error && sparse_write(fd, desc, desc_len) < 0) 205227825Stheraven error = errno; 206227825Stheraven if (!error && sparse_write(fd, gd, gdsz) < 0) 207249998Sdim error = errno; 208227825Stheraven if (!error && sparse_write(fd, gt, gtsz) < 0) 209253159Stheraven error = errno; 210253159Stheraven if (!error && sparse_write(fd, rgd, gdsz) < 0) 211227825Stheraven error = errno; 212227825Stheraven if (!error && sparse_write(fd, gt, gtsz) < 0) 213227825Stheraven error = errno; 214227825Stheraven free(gt); 215227825Stheraven free(rgd); 216227825Stheraven free(gd); 217227825Stheraven free(desc); 218227825Stheraven if (error) 219227825Stheraven return (error); 220232950Stheraven 221227825Stheraven cur = VMDK_SECTOR_SIZE + desc_len + (gdsz + gtsz) * 2; 222232950Stheraven lim = sec * VMDK_SECTOR_SIZE; 223227825Stheraven if (cur < lim) { 224227825Stheraven buf = calloc(1, VMDK_SECTOR_SIZE); 225227825Stheraven if (buf == NULL) 226227825Stheraven error = ENOMEM; 227249998Sdim while (!error && cur < lim) { 228227825Stheraven if (sparse_write(fd, buf, VMDK_SECTOR_SIZE) < 0) 229227825Stheraven error = errno; 230227825Stheraven cur += VMDK_SECTOR_SIZE; 231227825Stheraven } 232227825Stheraven if (buf != NULL) 233227825Stheraven free(buf); 234227825Stheraven } 235227825Stheraven if (error) 236227825Stheraven return (error); 237227825Stheraven 238227825Stheraven blkcnt = (grainsz * VMDK_SECTOR_SIZE) / secsz; 239227825Stheraven for (n = 0; n < ngrains; n++) { 240227825Stheraven blkofs = n * blkcnt; 241227825Stheraven if (image_data(blkofs, blkcnt)) { 242227825Stheraven error = image_copyout_region(fd, blkofs, blkcnt); 243227825Stheraven if (error) 244227825Stheraven return (error); 245227825Stheraven } 246227825Stheraven } 247227825Stheraven return (image_copyout_done(fd)); 248227825Stheraven} 249227825Stheraven 250227825Stheravenstatic struct mkimg_format vmdk_format = { 251227825Stheraven .name = "vmdk", 252227825Stheraven .description = "Virtual Machine Disk", 253227825Stheraven .resize = vmdk_resize, 254227825Stheraven .write = vmdk_write, 255227825Stheraven}; 256227825Stheraven 257227825StheravenFORMAT_DEFINE(vmdk_format); 258227825Stheraven