diff --git a/tools/vhd-tools/Makefile b/tools/vhd-tools/Makefile new file mode 100644 index 00000000000..2558aa15ae3 --- /dev/null +++ b/tools/vhd-tools/Makefile @@ -0,0 +1,17 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += $(CFLAGS_libxenctrl) +LDFLAGS += $(LDFLAGS_libxenctrl) + +SUBDIRS-y := +SUBDIRS-y += include +SUBDIRS-y += lvm +SUBDIRS-y += vhd +SUBDIRS-y += drivers + +clean: + rm -rf *.a *.so *.o *.rpm $(LIB) *~ $(DEPS) TAGS + +.PHONY: all clean install +all clean install: %: subdirs-% diff --git a/tools/vhd-tools/README b/tools/vhd-tools/README new file mode 100644 index 00000000000..75fc614e31b --- /dev/null +++ b/tools/vhd-tools/README @@ -0,0 +1,321 @@ +Blktap2 Userspace Tools + Library +================================ + +Dutch Meyer +4th June 2009 + +Andrew Warfield and Julian Chesterfield +16th June 2006 + + +The blktap2 userspace toolkit provides a user-level disk I/O +interface. The blktap2 mechanism involves a kernel driver that acts +similarly to the existing Xen/Linux blkback driver, and a set of +associated user-level libraries. Using these tools, blktap2 allows +virtual block devices presented to VMs to be implemented in userspace +and to be backed by raw partitions, files, network, etc. + +The key benefit of blktap2 is that it makes it easy and fast to write +arbitrary block backends, and that these user-level backends actually +perform very well. Specifically: + +- Metadata disk formats such as Copy-on-Write, encrypted disks, sparse + formats and other compression features can be easily implemented. + +- Accessing file-based images from userspace avoids problems related + to flushing dirty pages which are present in the Linux loopback + driver. (Specifically, doing a large number of writes to an + NFS-backed image don't result in the OOM killer going berserk.) + +- Per-disk handler processes enable easier userspace policing of block + resources, and process-granularity QoS techniques (disk scheduling + and related tools) may be trivially applied to block devices. + +- It's very easy to take advantage of userspace facilities such as + networking libraries, compression utilities, peer-to-peer + file-sharing systems and so on to build more complex block backends. + +- Crashes are contained -- incremental development/debugging is very + fast. + +How it works (in one paragraph): + +Working in conjunction with the kernel blktap2 driver, all disk I/O +requests from VMs are passed to the userspace deamon (using a shared +memory interface) through a character device. Each active disk is +mapped to an individual device node, allowing per-disk processes to +implement individual block devices where desired. The userspace +drivers are implemented using asynchronous (Linux libaio), +O_DIRECT-based calls to preserve the unbuffered, batched and +asynchronous request dispatch achieved with the existing blkback +code. We provide a simple, asynchronous virtual disk interface that +makes it quite easy to add new disk implementations. + +As of June 2009 the current supported disk formats are: + + - Raw Images (both on partitions and in image files) + - Fast sharable RAM disk between VMs (requires some form of + cluster-based filesystem support e.g. OCFS2 in the guest kernel) + - VHD, including snapshots and sparse images + - Qcow, including snapshots and sparse images + + +Build and Installation Instructions +=================================== + +Make to configure the blktap2 backend driver in your dom0 kernel. It +will inter-operate with the existing backend and frontend drivers. It +will also cohabitate with the original blktap driver. However, some +formats (currently aio and qcow) will default to their blktap2 +versions when specified in a vm configuration file. + +To build the tools separately, "make && make install" in +tools/blktap2. + + +Using the Tools +=============== + +Preparing an image for boot: + +The userspace disk agent is configured to start automatically via xend + +Customize the VM config file to use the 'tap:tapdisk' handler, +followed by the driver type. e.g. for a raw image such as a file or +partition: + +disk = ['tap:tapdisk:aio:,sda1,w'] + +Alternatively, the vhd-util tool (installed with make install, or in +/blktap2/vhd) can be used to build sparse copy-on-write vhd images. + +For example, to build a sparse image - + vhd-util create -n MyVHDFile -s 1024 + +This creates a sparse 1GB file named "MyVHDFile" that can be mounted +and populated with data. + +One can also base the image on a raw file - + vhd-util snapshot -n MyVHDFile -p SomeRawFile -m + +This creates a sparse VHD file named "MyVHDFile" using "SomeRawFile" +as a parent image. Copy-on-write semantics ensure that writes will be +stored in "MyVHDFile" while reads will be directed to the most +recently written version of the data, either in "MyVHDFile" or +"SomeRawFile" as is appropriate. Other options exist as well, consult +the vhd-util application for the complete set of VHD tools. + +VHD files can be mounted automatically in a guest similarly to the +above AIO example simply by specifying the vhd driver. + +disk = ['tap:tapdisk:vhd:,sda1,w'] + + +Snapshots: + +Pausing a guest will also plug the corresponding IO queue for blktap2 +devices and stop blktap2 drivers. This can be used to implement a +safe live snapshot of qcow and vhd disks. An example script "xmsnap" +is shown in the tools/blktap2/drivers directory. This script will +perform a live snapshot of a qcow disk. VHD files can use the +"vhd-util snapshot" tool discussed above. If this snapshot command is +applied to a raw file mounted with tap:tapdisk:AIO, include the -m +flag and the driver will be reloaded as VHD. If applied to an already +mounted VHD file, omit the -m flag. + + +Mounting images in Dom0 using the blktap2 driver +=============================================== +Tap (and blkback) disks are also mountable in Dom0 without requiring an +active VM to attach. + +The syntax is - + tapdisk2 -n : + +For example - + tapdisk2 -n aio:/home/images/rawFile.img + +When successful the location of the new device will be provided by +tapdisk2 to stdout and tapdisk2 will terminate. From that point +forward control of the device is provided through sysfs in the +directory- + + /sys/class/blktap2/blktap#/ + +Where # is a blktap2 device number present in the path that tapdisk2 +printed before terminating. The sysfs interface is largely intuitive, +for example, to remove tap device 0 one would- + + echo 1 > /sys/class/blktap2/blktap0/remove + +Similarly, a pause control is available, which is can be used to plug +the request queue of a live running guest. + +Previous versions of blktap mounted devices in dom0 by using blkfront +in dom0 and the xm block-attach command. This approach is still +available, though slightly more cumbersome. + + +Tapdisk Development +=============================================== + +People regularly ask how to develop their own tapdisk drivers, and +while it has not yet been well documented, the process is relatively +easy. Here I will provide a brief overview. The best reference, of +course, comes from the existing drivers. Specifically, +blktap2/drivers/block-ram.c and blktap2/drivers/block-aio.c provide +the clearest examples of simple drivers. + + +Setup: + +First you need to register your new driver with blktap. This is done +in disktypes.h. There are five things that you must do. To +demonstrate, I will create a disk called "mynewdisk", you can name +yours freely. + +1) Forward declare an instance of struct tap_disk. + +e.g. - + extern struct tap_disk tapdisk_mynewdisk; + +2) Claim one of the unused disk type numbers, take care to observe the +MAX_DISK_TYPES macro, increasing the number if necessary. + +e.g. - + #define DISK_TYPE_MYNEWDISK 10 + +3) Create an instance of disk_info_t. The bulk of this file contains examples of these. + +e.g. - + static disk_info_t mynewdisk_disk = { + DISK_TYPE_MYNEWDISK, + "My New Disk (mynewdisk)", + "mynewdisk", + 0, + #ifdef TAPDISK + &tapdisk_mynewdisk, + #endif + }; + +A few words about what these mean. The first field must be the disk +type number you claimed in step (2). The second field is a string +describing your disk, and may contain any relevant info. The third +field is the name of your disk as will be used by the tapdisk2 utility +and xend (for example tapdisk2 -n mynewdisk:/path/to/disk.image, or in +your xm create config file). The forth is binary and determines +whether you will have one instance of your driver, or many. Here, a 1 +means that your driver is a singleton and will coordinate access to +any number of tap devices. 0 is more common, meaning that you will +have one driver for each device that is created. The final field +should contain a reference to the struct tap_disk you created in step +(1). + +4) Add a reference to your disk info structure (from step (3)) to the +dtypes array. Take care here - you need to place it in the position +corresponding to the device type number you claimed in step (2). So +we would place &mynewdisk_disk in dtypes[10]. Look at the other +devices in this array and pad with "&null_disk," as necessary. + +5) Modify the xend python scripts. You need to add your disk name to +the list of disks that xend recognizes. + +edit: + tools/python/xen/xend/server/BlktapController.py + +And add your disk to the "blktap_disk_types" array near the top of +your file. Use the same name you specified in the third field of step +(3). The order of this list is not important. + + +Now your driver is ready to be written. Create a block-mynewdisk.c in +tools/blktap2/drivers and add it to the Makefile. + + +Development: + +Copying block-aio.c and block-ram.c would be a good place to start. +Read those files as you go through this, I will be assisting by +commenting on a few useful functions and structures. + +struct tap_disk: + +Remember the forward declaration in step (1) of the setup phase above? +Now is the time to make that structure a reality. This structure +contains a list of function pointers for all the routines that will be +asked of your driver. Currently the required functions are open, +close, read, write, get_parent_id, validate_parent, and debug. + +e.g. - + struct tap_disk tapdisk_mynewdisk = { + .disk_type = "tapdisk_mynewdisk", + .flags = 0, + .private_data_size = sizeof(struct tdmynewdisk_state), + .td_open = tdmynewdisk_open, + .... + +The private_data_size field is used to provide a structure to store +the state of your device. It is very likely that you will want +something here, but you are free to design whatever structure you +want. Blktap will allocate this space for you, you just need to tell +it how much space you want. + + +tdmynewdisk_open: + +This is the open routine. The first argument is a structure +representing your driver. Two fields in this array are +interesting. + +driver->data will contain a block of memory of the size your requested +in in the .private_data_size field of your struct tap_disk (above). + +driver->info contains a structure that details information about your +disk. You need to fill this out. By convention this is done with a +_get_image_info() function. Assign a size (the total number of +sectors), sector_size (the size of each sector in bytes, and set +driver->info->info to 0. + +The second parameter contains the name that was specified in the +creation of your device, either through xend, or on the command line +with tapdisk2. Usually this specifies a file that you will open in +this routine. The final parameter, flags, contains one of a number of +flags specified in tapdisk.h that may change the way you treat the +disk. + + +_queue_read/write: + +These are your read and write operations. What you do here will +depend on your disk, but you should do exactly one of- + +1) call td_complete_request with either error or success code. + +2) Call td_forward_request, which will forward the request to the next +driver in the stack. + +3) Queue the request for asynchronous processing with +td_prep_read/write. In doing so, you will also register a callback +for request completion. When the request completes you must do one of +options (1) or (2) above. Finally, call td_queue_tiocb to submit the +request to a wait queue. + +The above functions are defined in tapdisk-interface.c. If you don't +use them as specified you will run into problems as your driver will +fail to inform blktap of the state of requests that have been +submitted. Blktap keeps track of all requests and does not like losing track. + + +_close, _get_parent_id, _validate_parent: + +These last few tend to be very routine. _close is called when the +device is closed, and also when it is paused (in this case, open will +also be called later). The other functions are used in stacking +drivers. Most often drivers will return TD_NO_PARENT and -EINVAL, +respectively. + + + + + + diff --git a/tools/vhd-tools/include/Makefile b/tools/vhd-tools/include/Makefile new file mode 100644 index 00000000000..ca18512405f --- /dev/null +++ b/tools/vhd-tools/include/Makefile @@ -0,0 +1,14 @@ +XEN_ROOT := ../../.. +include $(XEN_ROOT)/tools/Rules.mk + +.PHONY: all +all: + +.PHONY: install +install: + $(INSTALL_DIR) -p $(DESTDIR)$(INCLUDEDIR) + + +.PHONY: clean +clean: + @: diff --git a/tools/vhd-tools/include/atomicio.h b/tools/vhd-tools/include/atomicio.h new file mode 100644 index 00000000000..7eccf206b3b --- /dev/null +++ b/tools/vhd-tools/include/atomicio.h @@ -0,0 +1,33 @@ +/* $OpenBSD: atomicio.h,v 1.6 2005/05/24 17:32:43 avsm Exp $ */ + +/* + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * 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 ``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 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. + */ + +/* + * Ensure all of data on socket comes through. f==read || f==vwrite + */ +size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); + +#define vwrite (ssize_t (*)(int, void *, size_t))write diff --git a/tools/vhd-tools/include/blk_uuid.h b/tools/vhd-tools/include/blk_uuid.h new file mode 100644 index 00000000000..9d554a9ce5f --- /dev/null +++ b/tools/vhd-tools/include/blk_uuid.h @@ -0,0 +1,130 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. +*/ +#ifndef __BLKTAP2_UUID_H__ +#define __BLKTAP2_UUID_H__ + +#if defined(__linux__) + +#include + +typedef struct { + uuid_t uuid; +} blk_uuid_t; + +static inline int blk_uuid_is_nil(blk_uuid_t *uuid) +{ + return uuid_is_null(uuid->uuid); +} + +static inline void blk_uuid_generate(blk_uuid_t *uuid) +{ + uuid_generate(uuid->uuid); +} + +static inline void blk_uuid_to_string(blk_uuid_t *uuid, char *out, size_t size) +{ + uuid_unparse(uuid->uuid, out); +} + +static inline void blk_uuid_from_string(blk_uuid_t *uuid, const char *in) +{ + uuid_parse(in, uuid->uuid); +} + +static inline void blk_uuid_copy(blk_uuid_t *dst, blk_uuid_t *src) +{ + uuid_copy(dst->uuid, src->uuid); +} + +static inline void blk_uuid_clear(blk_uuid_t *uuid) +{ + uuid_clear(uuid->uuid); +} + +static inline int blk_uuid_compare(blk_uuid_t *uuid1, blk_uuid_t *uuid2) +{ + return uuid_compare(uuid1->uuid, uuid2->uuid); +} + +#elif defined(__NetBSD__) + +#include +#include +#include + +typedef uuid_t blk_uuid_t; + +static inline int blk_uuid_is_nil(blk_uuid_t *uuid) +{ + uint32_t status; + return uuid_is_nil((uuid_t *)uuid, &status); +} + +static inline void blk_uuid_generate(blk_uuid_t *uuid) +{ + uint32_t status; + uuid_create((uuid_t *)uuid, &status); +} + +static inline void blk_uuid_to_string(blk_uuid_t *uuid, char *out, size_t size) +{ + uint32_t status; + char *_out = NULL; + uuid_to_string((uuid_t *)uuid, &_out, &status); + strlcpy(out, _out, size); + free(_out); +} + +static inline void blk_uuid_from_string(blk_uuid_t *uuid, const char *in) +{ + uint32_t status; + uuid_from_string(in, (uuid_t *)uuid, &status); +} + +static inline void blk_uuid_copy(blk_uuid_t *dst, blk_uuid_t *src) +{ + memcpy((uuid_t *)dst, (uuid_t *)src, sizeof(uuid_t)); +} + +static inline void blk_uuid_clear(blk_uuid_t *uuid) +{ + memset((uuid_t *)uuid, 0, sizeof(uuid_t)); +} + +static inline int blk_uuid_compare(blk_uuid_t *uuid1, blk_uuid_t *uuid2) +{ + uint32_t status; + return uuid_compare((uuid_t *)uuid1, (uuid_t *)uuid2, &status); +} + +#else + +#error "Please update blk_uuid.h for your OS" + +#endif + +#endif /* __BLKTAP2_UUID_H__ */ diff --git a/tools/vhd-tools/include/blktaplib.h b/tools/vhd-tools/include/blktaplib.h new file mode 100644 index 00000000000..d91a29b25f0 --- /dev/null +++ b/tools/vhd-tools/include/blktaplib.h @@ -0,0 +1,250 @@ +/* blktaplib.h + * + * Blktap library userspace code. + * + * Copyright (c) 2007, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. + */ + +#ifndef __BLKTAPLIB_H__ +#define __BLKTAPLIB_H__ + +#include +#include +#include + +#if 1 +#define DPRINTF(_f, _a...) syslog(LOG_INFO, _f, ##_a) +#else +#define DPRINTF(_f, _a...) ((void)0) +#endif + +#define EPRINTF(_f, _a...) syslog(LOG_ERR, "tap-err:%s: " _f, __func__, ##_a) +#define PERROR(_f, _a...) EPRINTF(_f ": %s", ##_a, strerror(errno)) + +#define BLK_RING_SIZE __CONST_RING_SIZE(blkif, XC_PAGE_SIZE) + +/* size of the extra VMA area to map in attached pages. */ +#define BLKTAP_VMA_PAGES BLK_RING_SIZE + +/* blktap IOCTLs: These must correspond with the blktap driver ioctls */ +#define BLKTAP_IOCTL_KICK_FE 1 +#define BLKTAP_IOCTL_KICK_BE 2 +#define BLKTAP_IOCTL_SETMODE 3 +#define BLKTAP_IOCTL_SENDPID 4 +#define BLKTAP_IOCTL_NEWINTF 5 +#define BLKTAP_IOCTL_MINOR 6 +#define BLKTAP_IOCTL_MAJOR 7 +#define BLKTAP_QUERY_ALLOC_REQS 8 +#define BLKTAP_IOCTL_FREEINTF 9 +#define BLKTAP_IOCTL_PRINT_IDXS 100 +#define BLKTAP_IOCTL_BACKDEV_SETUP 200 + +#define PRIO_SPECIAL_IO -9999 + +/* blktap switching modes: (Set with BLKTAP_IOCTL_SETMODE) */ +#define BLKTAP_MODE_PASSTHROUGH 0x00000000 /* default */ +#define BLKTAP_MODE_INTERCEPT_FE 0x00000001 +#define BLKTAP_MODE_INTERCEPT_BE 0x00000002 + +#define BLKTAP_MODE_INTERPOSE \ + (BLKTAP_MODE_INTERCEPT_FE | BLKTAP_MODE_INTERCEPT_BE) + +static inline int BLKTAP_MODE_VALID(unsigned long arg) +{ + return ( + ( arg == BLKTAP_MODE_PASSTHROUGH ) || + ( arg == BLKTAP_MODE_INTERCEPT_FE ) || + ( arg == BLKTAP_MODE_INTERPOSE ) ); +} + +#define MAX_REQUESTS BLK_RING_SIZE + +#define BLKTAP_IOCTL_KICK 1 +#define MAX_PENDING_REQS BLK_RING_SIZE +#define BLKTAP_DEV_DIR "/dev/xen" +#define BLKTAP_DEV_NAME "blktap" +#define BACKDEV_NAME "backdev" +#define BLKTAP_DEV_MINOR 0 +#define BLKTAP_CTRL_DIR "/var/run/tap" + +extern int blktap_major; + +#define BLKTAP_RING_PAGES 1 /* Front */ +#define BLKTAP_MMAP_REGION_SIZE (BLKTAP_RING_PAGES + MMAP_PAGES) + +struct blkif; +struct blkif_info; + +typedef struct { + blkif_request_t req; + int submitting; + int secs_pending; + int16_t status; + int num_retries; + struct timeval last_try; +} pending_req_t; + +typedef struct blkif { + domid_t domid; + long int handle; + + long int pdev; + long int readonly; + + enum { DISCONNECTED, DISCONNECTING, CONNECTED } state; + + struct blkif_ops *ops; + struct blkif *hash_next; + + void *prv; /* device-specific data */ + struct blkif_info *info; /*Image parameter passing */ + pending_req_t pending_list[MAX_REQUESTS]; + int devnum; + int fds[2]; + int be_id; + char *backend_path; + int major; + int minor; + pid_t tappid; + int drivertype; + uint16_t cookie; + int err; +} blkif_t; + +typedef struct blkif_info { + char *params; + int readonly; + int storage; +} blkif_info_t; + +typedef struct tapdev_info { + int fd; + char *mem; + blkif_sring_t *sring; + blkif_back_ring_t fe_ring; + unsigned long vstart; + blkif_t *blkif; +} tapdev_info_t; + +typedef struct domid_translate { + unsigned short domid; + unsigned short busid; +} domid_translate_t ; + +typedef struct image { + unsigned long long size; + unsigned long secsize; + unsigned int info; +} image_t; + +typedef struct msg_hdr { + uint16_t type; + uint16_t len; + uint16_t drivertype; + uint16_t cookie; +} msg_hdr_t; + +typedef struct msg_params { + uint8_t readonly; + int path_off; + int path_len; + int storage; +} msg_params_t; + +typedef struct msg_newdev { + uint8_t devnum; + uint16_t domid; +} msg_newdev_t; + +typedef struct msg_pid { + pid_t pid; +} msg_pid_t; + +typedef struct msg_cp { + int cp_uuid_off; + int cp_uuid_len; + int cp_drivertype; +} msg_cp_t; + +typedef struct msg_lock { + int ro; + int enforce; + int uuid_off; + int uuid_len; +} msg_lock_t; + +#define READ 0 +#define WRITE 1 + +/*Control Messages between manager and tapdev*/ +#define CTLMSG_PARAMS 1 +#define CTLMSG_IMG 2 +#define CTLMSG_IMG_FAIL 3 +#define CTLMSG_NEWDEV 4 +#define CTLMSG_NEWDEV_RSP 5 +#define CTLMSG_NEWDEV_FAIL 6 +#define CTLMSG_CLOSE 7 +#define CTLMSG_CLOSE_RSP 8 +#define CTLMSG_PID 9 +#define CTLMSG_PID_RSP 10 +#define CTLMSG_CHECKPOINT 11 +#define CTLMSG_CHECKPOINT_RSP 12 +#define CTLMSG_LOCK 13 +#define CTLMSG_LOCK_RSP 14 +#define CTLMSG_PAUSE 15 +#define CTLMSG_PAUSE_RSP 16 +#define CTLMSG_RESUME 17 +#define CTLMSG_RESUME_RSP 18 + +#define TAPDISK_STORAGE_TYPE_NFS 1 +#define TAPDISK_STORAGE_TYPE_EXT 2 +#define TAPDISK_STORAGE_TYPE_LVM 3 +#define TAPDISK_STORAGE_TYPE_DEFAULT TAPDISK_STORAGE_TYPE_EXT + +/* Abitrary values, must match the underlying driver... */ +#define MAX_TAP_DEV 256 + +/* Accessing attached data page mappings */ +#define MMAP_PAGES \ + (MAX_PENDING_REQS * BLKIF_MAX_SEGMENTS_PER_REQUEST) +#define MMAP_VADDR(_vstart,_req,_seg) \ + ((_vstart) + \ + ((_req) * BLKIF_MAX_SEGMENTS_PER_REQUEST * getpagesize()) + \ + ((_seg) * getpagesize())) + +/* Defines that are only used by library clients */ + +#ifndef __COMPILING_BLKTAP_LIB + +static char *blkif_op_name[] = { + [BLKIF_OP_READ] = "READ", + [BLKIF_OP_WRITE] = "WRITE", +}; + +#endif /* __COMPILING_BLKTAP_LIB */ + +#endif /* __BLKTAPLIB_H__ */ diff --git a/tools/vhd-tools/include/libvhd-journal.h b/tools/vhd-tools/include/libvhd-journal.h new file mode 100644 index 00000000000..75852523c9f --- /dev/null +++ b/tools/vhd-tools/include/libvhd-journal.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. +*/ +#ifndef _VHD_JOURNAL_H_ +#define _VHD_JOURNAL_H_ + +#include + +#include "libvhd.h" + +#define VHD_JOURNAL_METADATA 0x01 +#define VHD_JOURNAL_DATA 0x02 + +#define VHD_JOURNAL_HEADER_COOKIE "vjournal" +#define VHD_JOURNAL_ENTRY_COOKIE 0xaaaa12344321aaaa + +typedef struct vhd_journal_header { + char cookie[8]; + blk_uuid_t uuid; + uint64_t vhd_footer_offset; + uint32_t journal_data_entries; + uint32_t journal_metadata_entries; + uint64_t journal_data_offset; + uint64_t journal_metadata_offset; + uint64_t journal_eof; + char pad[448]; +} vhd_journal_header_t; + +typedef struct vhd_journal { + char *jname; + int jfd; + int is_block; /* is jfd a block device */ + vhd_journal_header_t header; + vhd_context_t vhd; +} vhd_journal_t; + +int vhd_journal_create(vhd_journal_t *, const char *file, const char *jfile); +int vhd_journal_open(vhd_journal_t *, const char *file, const char *jfile); +int vhd_journal_add_block(vhd_journal_t *, uint32_t block, char mode); +int vhd_journal_commit(vhd_journal_t *); +int vhd_journal_revert(vhd_journal_t *); +int vhd_journal_close(vhd_journal_t *); +int vhd_journal_remove(vhd_journal_t *); + +#endif diff --git a/tools/vhd-tools/include/libvhd.h b/tools/vhd-tools/include/libvhd.h new file mode 100644 index 00000000000..ac6908415ee --- /dev/null +++ b/tools/vhd-tools/include/libvhd.h @@ -0,0 +1,326 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. +*/ +#ifndef _VHD_LIB_H_ +#define _VHD_LIB_H_ + +#include +#if defined(__linux__) +#include +#include +#elif defined(__NetBSD__) +#include +#include +#endif + +#include "blk_uuid.h" +#include "vhd.h" + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN +#if defined(__linux__) + #define BE16_IN(foo) (*(foo)) = bswap_16(*(foo)) + #define BE32_IN(foo) (*(foo)) = bswap_32(*(foo)) + #define BE64_IN(foo) (*(foo)) = bswap_64(*(foo)) + #define BE16_OUT(foo) (*(foo)) = bswap_16(*(foo)) + #define BE32_OUT(foo) (*(foo)) = bswap_32(*(foo)) + #define BE64_OUT(foo) (*(foo)) = bswap_64(*(foo)) +#elif defined(__NetBSD__) + #define BE16_IN(foo) (*(foo)) = bswap16(*(foo)) + #define BE32_IN(foo) (*(foo)) = bswap32(*(foo)) + #define BE64_IN(foo) (*(foo)) = bswap64(*(foo)) + #define BE16_OUT(foo) (*(foo)) = bswap16(*(foo)) + #define BE32_OUT(foo) (*(foo)) = bswap32(*(foo)) + #define BE64_OUT(foo) (*(foo)) = bswap64(*(foo)) +#endif +#else + #define BE16_IN(foo) + #define BE32_IN(foo) + #define BE64_IN(foo) + #define BE32_OUT(foo) + #define BE32_OUT(foo) + #define BE64_OUT(foo) +#endif + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define VHD_MAX_NAME_LEN 1024 + +#define VHD_BLOCK_SHIFT 21 +#define VHD_BLOCK_SIZE (1ULL << VHD_BLOCK_SHIFT) + +#define UTF_16 "UTF-16" +#define UTF_16LE "UTF-16LE" +#define UTF_16BE "UTF-16BE" + +#define VHD_OPEN_RDONLY 0x00001 +#define VHD_OPEN_RDWR 0x00002 +#define VHD_OPEN_FAST 0x00004 +#define VHD_OPEN_STRICT 0x00008 +#define VHD_OPEN_IGNORE_DISABLED 0x00010 + +#define VHD_FLAG_CREAT_PARENT_RAW 0x00001 + +#define vhd_flag_set(word, flag) ((word) |= (flag)) +#define vhd_flag_clear(word, flag) ((word) &= ~(flag)) +#define vhd_flag_test(word, flag) ((word) & (flag)) + + +#define ENABLE_FAILURE_TESTING +#define FAIL_REPARENT_BEGIN 0 +#define FAIL_REPARENT_LOCATOR 1 +#define FAIL_REPARENT_END 2 +#define FAIL_RESIZE_BEGIN 3 +#define FAIL_RESIZE_DATA_MOVED 4 +#define FAIL_RESIZE_METADATA_MOVED 5 +#define FAIL_RESIZE_END 6 +#define NUM_FAIL_TESTS 7 + +#ifdef ENABLE_FAILURE_TESTING +#define TEST_FAIL_AT(point) \ + if (TEST_FAIL[point]) { \ + printf("Failing at %s\n", ENV_VAR_FAIL[point]); exit(EINVAL); } +#define TEST_FAIL_EXTERN_VARS \ + extern const char* ENV_VAR_FAIL[]; \ + extern int TEST_FAIL[]; +#else +#define TEST_FAIL_AT(point) +#define TEST_FAIL_EXTERN_VARS +#endif // ENABLE_FAILURE_TESTING + + +static const char VHD_POISON_COOKIE[] = "v_poison"; + +typedef struct hd_ftr vhd_footer_t; +typedef struct dd_hdr vhd_header_t; +typedef struct vhd_bat vhd_bat_t; +typedef struct vhd_batmap vhd_batmap_t; +typedef struct dd_batmap_hdr vhd_batmap_header_t; +typedef struct prt_loc vhd_parent_locator_t; +typedef struct vhd_context vhd_context_t; +typedef uint32_t vhd_flag_creat_t; + +struct vhd_bat { + uint32_t spb; + uint32_t entries; + uint32_t *bat; +}; + +struct vhd_batmap { + vhd_batmap_header_t header; + char *map; +}; + +struct vhd_context { + int fd; + char *file; + int oflags; + int is_block; + + uint32_t spb; + uint32_t bm_secs; + + vhd_header_t header; + vhd_footer_t footer; + vhd_bat_t bat; + vhd_batmap_t batmap; +}; + +static inline uint32_t +secs_round_up(uint64_t bytes) +{ + return ((bytes + (VHD_SECTOR_SIZE - 1)) >> VHD_SECTOR_SHIFT); +} + +static inline uint32_t +secs_round_up_no_zero(uint64_t bytes) +{ + return (secs_round_up(bytes) ? : 1); +} + +static inline uint64_t +vhd_sectors_to_bytes(uint64_t sectors) +{ + return sectors << VHD_SECTOR_SHIFT; +} + +static inline uint64_t +vhd_bytes_padded(uint64_t bytes) +{ + return vhd_sectors_to_bytes(secs_round_up_no_zero(bytes)); +} + +static inline int +vhd_type_dynamic(vhd_context_t *ctx) +{ + return (ctx->footer.type == HD_TYPE_DYNAMIC || + ctx->footer.type == HD_TYPE_DIFF); +} + +static inline int +vhd_creator_tapdisk(vhd_context_t *ctx) +{ + return !strncmp(ctx->footer.crtr_app, "tap", 3); +} + +static inline int +vhd_disabled(vhd_context_t *ctx) +{ + return (!memcmp(ctx->footer.cookie, + VHD_POISON_COOKIE, sizeof(ctx->footer.cookie))); +} + +static inline size_t +vhd_parent_locator_size(vhd_parent_locator_t *loc) +{ + /* + * MICROSOFT_COMPAT + * data_space *should* be in sectors, + * but sometimes we find it in bytes + */ + if (loc->data_space < 512) + return vhd_sectors_to_bytes(loc->data_space); + else if (loc->data_space % 512 == 0) + return loc->data_space; + else + return 0; +} + +static inline int +vhd_parent_raw(vhd_context_t *ctx) +{ + return blk_uuid_is_nil(&ctx->header.prt_uuid); +} + +void libvhd_set_log_level(int); + +int vhd_test_file_fixed(const char *, int *); + +uint32_t vhd_time(time_t time); +size_t vhd_time_to_string(uint32_t timestamp, char *target); +uint32_t vhd_chs(uint64_t size); + +uint32_t vhd_checksum_footer(vhd_footer_t *); +uint32_t vhd_checksum_header(vhd_header_t *); +uint32_t vhd_checksum_batmap(vhd_batmap_t *); + +void vhd_footer_in(vhd_footer_t *); +void vhd_footer_out(vhd_footer_t *); +void vhd_header_in(vhd_header_t *); +void vhd_header_out(vhd_header_t *); +void vhd_bat_in(vhd_bat_t *); +void vhd_bat_out(vhd_bat_t *); +void vhd_batmap_header_in(vhd_batmap_t *); +void vhd_batmap_header_out(vhd_batmap_t *); + +int vhd_validate_footer(vhd_footer_t *footer); +int vhd_validate_header(vhd_header_t *header); +int vhd_validate_batmap_header(vhd_batmap_t *batmap); +int vhd_validate_batmap(vhd_batmap_t *batmap); +int vhd_validate_platform_code(uint32_t code); + +int vhd_open(vhd_context_t *, const char *file, int flags); +void vhd_close(vhd_context_t *); +int vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t); +/* vhd_snapshot: the bytes parameter is optional and can be 0 if the snapshot + * is to have the same size as the (first non-empty) parent */ +int vhd_snapshot(const char *snapshot, uint64_t bytes, const char *parent, + vhd_flag_creat_t); + +int vhd_hidden(vhd_context_t *, int *); +int vhd_chain_depth(vhd_context_t *, int *); + +off_t vhd_position(vhd_context_t *); +int vhd_seek(vhd_context_t *, off_t, int); +int vhd_read(vhd_context_t *, void *, size_t); +int vhd_write(vhd_context_t *, void *, size_t); + +int vhd_offset(vhd_context_t *, uint32_t, uint32_t *); + +int vhd_end_of_headers(vhd_context_t *ctx, off_t *off); +int vhd_end_of_data(vhd_context_t *ctx, off_t *off); +int vhd_batmap_header_offset(vhd_context_t *ctx, off_t *off); + +int vhd_get_header(vhd_context_t *); +int vhd_get_footer(vhd_context_t *); +int vhd_get_bat(vhd_context_t *); +int vhd_get_batmap(vhd_context_t *); + +void vhd_put_header(vhd_context_t *); +void vhd_put_footer(vhd_context_t *); +void vhd_put_bat(vhd_context_t *); +void vhd_put_batmap(vhd_context_t *); + +int vhd_has_batmap(vhd_context_t *); +int vhd_batmap_test(vhd_context_t *, vhd_batmap_t *, uint32_t); +void vhd_batmap_set(vhd_context_t *, vhd_batmap_t *, uint32_t); +void vhd_batmap_clear(vhd_context_t *, vhd_batmap_t *, uint32_t); + +int vhd_get_phys_size(vhd_context_t *, off_t *); +int vhd_set_phys_size(vhd_context_t *, off_t); + +int vhd_bitmap_test(vhd_context_t *, char *, uint32_t); +void vhd_bitmap_set(vhd_context_t *, char *, uint32_t); +void vhd_bitmap_clear(vhd_context_t *, char *, uint32_t); + +int vhd_parent_locator_count(vhd_context_t *); +int vhd_parent_locator_get(vhd_context_t *, char **); +int vhd_parent_locator_read(vhd_context_t *, vhd_parent_locator_t *, char **); +int vhd_find_parent(vhd_context_t *, const char *, char **); +int vhd_parent_locator_write_at(vhd_context_t *, const char *, + off_t, uint32_t, size_t, + vhd_parent_locator_t *); + +int vhd_header_decode_parent(vhd_context_t *, vhd_header_t *, char **); +int vhd_change_parent(vhd_context_t *, char *parent_path, int raw); + +int vhd_read_footer(vhd_context_t *, vhd_footer_t *); +int vhd_read_footer_at(vhd_context_t *, vhd_footer_t *, off_t); +int vhd_read_footer_strict(vhd_context_t *, vhd_footer_t *); +int vhd_read_header(vhd_context_t *, vhd_header_t *); +int vhd_read_header_at(vhd_context_t *, vhd_header_t *, off_t); +int vhd_read_bat(vhd_context_t *, vhd_bat_t *); +int vhd_read_batmap(vhd_context_t *, vhd_batmap_t *); +int vhd_read_bitmap(vhd_context_t *, uint32_t block, char **bufp); +int vhd_read_block(vhd_context_t *, uint32_t block, char **bufp); + +int vhd_write_footer(vhd_context_t *, vhd_footer_t *); +int vhd_write_footer_at(vhd_context_t *, vhd_footer_t *, off_t); +int vhd_write_header(vhd_context_t *, vhd_header_t *); +int vhd_write_header_at(vhd_context_t *, vhd_header_t *, off_t); +int vhd_write_bat(vhd_context_t *, vhd_bat_t *); +int vhd_write_batmap(vhd_context_t *, vhd_batmap_t *); +int vhd_write_bitmap(vhd_context_t *, uint32_t block, char *bitmap); +int vhd_write_block(vhd_context_t *, uint32_t block, char *data); + +int vhd_io_read(vhd_context_t *, char *, uint64_t, uint32_t); +int vhd_io_write(vhd_context_t *, char *, uint64_t, uint32_t); + +#endif diff --git a/tools/vhd-tools/include/list.h b/tools/vhd-tools/include/list.h new file mode 100644 index 00000000000..2a5f8557434 --- /dev/null +++ b/tools/vhd-tools/include/list.h @@ -0,0 +1,124 @@ +/* + * list.h + * + * This is a subset of linux's list.h intended to be used in user-space. + * XXX The namespace conflicts with NetBSD's + * + */ + +#ifndef __LIST_H__ +#define __LIST_H__ + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +struct list_head { + struct list_head *next, *prev; +}; + +/* XXX workaround for conflicts. The list API should use its own + * namespace prefix, i.e. BLK_ + */ +#ifdef LIST_HEAD_INIT +#undef LIST_HEAD_INIT +#endif +#ifndef LIST_HEAD +#undef LIST_HEAD +#endif + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif /* __LIST_H__ */ diff --git a/tools/vhd-tools/include/lvm-util.h b/tools/vhd-tools/include/lvm-util.h new file mode 100644 index 00000000000..95f33203349 --- /dev/null +++ b/tools/vhd-tools/include/lvm-util.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. + */ +#ifndef _LVM_UTIL_H_ +#define _LVM_UTIL_H_ + +#include + +#define MAX_NAME_SIZE 256 + +#define LVM_SEG_TYPE_LINEAR 1 +#define LVM_SEG_TYPE_UNKNOWN 2 + +struct lv_segment { + uint8_t type; + char device[MAX_NAME_SIZE]; + uint64_t pe_start; + uint64_t pe_size; +}; + +struct lv { + char name[MAX_NAME_SIZE]; + uint64_t size; + uint32_t segments; + struct lv_segment first_segment; +}; + +struct pv { + char name[MAX_NAME_SIZE]; + uint64_t start; +}; + +struct vg { + char name[MAX_NAME_SIZE]; + uint64_t extent_size; + + int pv_cnt; + struct pv *pvs; + + int lv_cnt; + struct lv *lvs; +}; + +int lvm_scan_vg(const char *vg_name, struct vg *vg); +void lvm_free_vg(struct vg *vg); + +#endif diff --git a/tools/vhd-tools/include/relative-path.h b/tools/vhd-tools/include/relative-path.h new file mode 100644 index 00000000000..d78f94d0233 --- /dev/null +++ b/tools/vhd-tools/include/relative-path.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. +*/ +#ifndef _RELATIVE_PATH_H_ +#define _RELATIVE_PATH_H_ + +#include + +#define DELIMITER '/' +#define MAX_NAME_LEN 1000 + +#define EPRINTF(_f, _a...) syslog(LOG_ERR, "tap-err:%s: " _f, __func__, ##_a) + +/* + * returns a relative path from @src to @dest + * result should be freed + */ +char *relative_path_to(char *src, char *dest, int *err); + +#endif diff --git a/tools/vhd-tools/include/tapdisk-message.h b/tools/vhd-tools/include/tapdisk-message.h new file mode 100644 index 00000000000..1a86dcb6a31 --- /dev/null +++ b/tools/vhd-tools/include/tapdisk-message.h @@ -0,0 +1,141 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. +*/ +#ifndef _TAPDISK_MESSAGE_H_ +#define _TAPDISK_MESSAGE_H_ + +#include + +#define TAPDISK_MESSAGE_MAX_PATH_LENGTH 256 +#define TAPDISK_MESSAGE_STRING_LENGTH 256 + +#define TAPDISK_MESSAGE_FLAG_SHARED 0x01 +#define TAPDISK_MESSAGE_FLAG_RDONLY 0x02 +#define TAPDISK_MESSAGE_FLAG_ADD_CACHE 0x04 +#define TAPDISK_MESSAGE_FLAG_VHD_INDEX 0x08 +#define TAPDISK_MESSAGE_FLAG_LOG_DIRTY 0x10 + +typedef struct tapdisk_message tapdisk_message_t; +typedef uint8_t tapdisk_message_flag_t; +typedef struct tapdisk_message_image tapdisk_message_image_t; +typedef struct tapdisk_message_params tapdisk_message_params_t; +typedef struct tapdisk_message_string tapdisk_message_string_t; + +struct tapdisk_message_params { + tapdisk_message_flag_t flags; + + uint8_t storage; + uint32_t devnum; + uint32_t domid; + uint16_t path_len; + char path[TAPDISK_MESSAGE_MAX_PATH_LENGTH]; +}; + +struct tapdisk_message_image { + uint64_t sectors; + uint32_t sector_size; + uint32_t info; +}; + +struct tapdisk_message_string { + char text[TAPDISK_MESSAGE_STRING_LENGTH]; +}; + +struct tapdisk_message { + uint16_t type; + uint16_t cookie; + uint16_t drivertype; + + union { + pid_t tapdisk_pid; + tapdisk_message_image_t image; + tapdisk_message_params_t params; + tapdisk_message_string_t string; + } u; +}; + +enum tapdisk_message_id { + TAPDISK_MESSAGE_ERROR = 1, + TAPDISK_MESSAGE_RUNTIME_ERROR, + TAPDISK_MESSAGE_PID, + TAPDISK_MESSAGE_PID_RSP, + TAPDISK_MESSAGE_OPEN, + TAPDISK_MESSAGE_OPEN_RSP, + TAPDISK_MESSAGE_PAUSE, + TAPDISK_MESSAGE_PAUSE_RSP, + TAPDISK_MESSAGE_RESUME, + TAPDISK_MESSAGE_RESUME_RSP, + TAPDISK_MESSAGE_CLOSE, + TAPDISK_MESSAGE_CLOSE_RSP, + TAPDISK_MESSAGE_EXIT, +}; + +static inline char * +tapdisk_message_name(enum tapdisk_message_id id) +{ + switch (id) { + case TAPDISK_MESSAGE_ERROR: + return "error"; + + case TAPDISK_MESSAGE_PID: + return "pid"; + + case TAPDISK_MESSAGE_PID_RSP: + return "pid response"; + + case TAPDISK_MESSAGE_OPEN: + return "open"; + + case TAPDISK_MESSAGE_OPEN_RSP: + return "open response"; + + case TAPDISK_MESSAGE_PAUSE: + return "pause"; + + case TAPDISK_MESSAGE_PAUSE_RSP: + return "pause response"; + + case TAPDISK_MESSAGE_RESUME: + return "resume"; + + case TAPDISK_MESSAGE_RESUME_RSP: + return "resume response"; + + case TAPDISK_MESSAGE_CLOSE: + return "close"; + + case TAPDISK_MESSAGE_CLOSE_RSP: + return "close response"; + + case TAPDISK_MESSAGE_EXIT: + return "exit"; + + default: + return "unknown"; + } +} + +#endif diff --git a/tools/vhd-tools/include/vhd-util.h b/tools/vhd-tools/include/vhd-util.h new file mode 100644 index 00000000000..11f077e2bfb --- /dev/null +++ b/tools/vhd-tools/include/vhd-util.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. +*/ +#ifndef _VHD_UTIL_H_ +#define _VHD_UTIL_H_ + +int vhd_util_create(int argc, char **argv); +int vhd_util_snapshot(int argc, char **argv); +int vhd_util_query(int argc, char **argv); +int vhd_util_read(int argc, char **argv); +int vhd_util_set_field(int argc, char **argv); +int vhd_util_repair(int argc, char **argv); +int vhd_util_fill(int argc, char **argv); +int vhd_util_resize(int argc, char **argv); +int vhd_util_coalesce(int argc, char **argv); +int vhd_util_modify(int argc, char **argv); +int vhd_util_scan(int argc, char **argv); +int vhd_util_check(int argc, char **argv); +int vhd_util_revert(int argc, char **argv); + +#endif diff --git a/tools/vhd-tools/include/vhd.h b/tools/vhd-tools/include/vhd.h new file mode 100644 index 00000000000..a31aadf491e --- /dev/null +++ b/tools/vhd-tools/include/vhd.h @@ -0,0 +1,219 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. +*/ +#ifndef __VHD_H__ +#define __VHD_H__ + +#include + +typedef uint32_t u32; +typedef uint64_t u64; + +#define DEBUG 1 + +/* ---------------------------------------------------------------------- */ +/* General definitions. */ +/* ---------------------------------------------------------------------- */ + +#define VHD_SECTOR_SIZE 512 +#define VHD_SECTOR_SHIFT 9 + +/* ---------------------------------------------------------------------- */ +/* This is the generic disk footer, used by all disks. */ +/* ---------------------------------------------------------------------- */ + +struct hd_ftr { + char cookie[8]; /* Identifies original creator of the disk */ + u32 features; /* Feature Support -- see below */ + u32 ff_version; /* (major,minor) version of disk file */ + u64 data_offset; /* Abs. offset from SOF to next structure */ + u32 timestamp; /* Creation time. secs since 1/1/2000GMT */ + char crtr_app[4]; /* Creator application */ + u32 crtr_ver; /* Creator version (major,minor) */ + u32 crtr_os; /* Creator host OS */ + u64 orig_size; /* Size at creation (bytes) */ + u64 curr_size; /* Current size of disk (bytes) */ + u32 geometry; /* Disk geometry */ + u32 type; /* Disk type */ + u32 checksum; /* 1's comp sum of this struct. */ + blk_uuid_t uuid; /* Unique disk ID, used for naming parents */ + char saved; /* one-bit -- is this disk/VM in a saved state? */ + char hidden; /* tapdisk-specific field: is this vdi hidden? */ + char reserved[426]; /* padding */ +}; + +/* VHD cookie string. */ +static const char HD_COOKIE[9] = "conectix"; + +/* Feature fields in hd_ftr */ +#define HD_NO_FEATURES 0x00000000 +#define HD_TEMPORARY 0x00000001 /* disk can be deleted on shutdown */ +#define HD_RESERVED 0x00000002 /* NOTE: must always be set */ + +/* Version field in hd_ftr */ +#define HD_FF_VERSION 0x00010000 + +/* Known creator OS type fields in hd_ftr.crtr_os */ +#define HD_CR_OS_WINDOWS 0x5769326B /* (Wi2k) */ +#define HD_CR_OS_MACINTOSH 0x4D616320 /* (Mac ) */ + +/* + * version 0.1: little endian bitmaps + * version 1.1: big endian bitmaps; batmap + * version 1.2: libvhd + * version 1.3: batmap version bump to 1.2 + */ +#define VHD_VERSION(major, minor) (((major) << 16) | ((minor) & 0x0000FFFF)) +#define VHD_CURRENT_VERSION VHD_VERSION(1, 3) + +/* Disk geometry accessor macros. */ +/* Geometry is a triple of (cylinders (2 bytes), tracks (1 byte), and + * secotrs-per-track (1 byte)) + */ +#define GEOM_GET_CYLS(_g) (((_g) >> 16) & 0xffff) +#define GEOM_GET_HEADS(_g) (((_g) >> 8) & 0xff) +#define GEOM_GET_SPT(_g) ((_g) & 0xff) + +#define GEOM_ENCODE(_c, _h, _s) (((_c) << 16) | ((_h) << 8) | (_s)) + +/* type field in hd_ftr */ +#define HD_TYPE_NONE 0 +#define HD_TYPE_FIXED 2 /* fixed-allocation disk */ +#define HD_TYPE_DYNAMIC 3 /* dynamic disk */ +#define HD_TYPE_DIFF 4 /* differencing disk */ + +/* String table for hd.type */ +static const char *HD_TYPE_STR[7] = { + "None", /* 0 */ + "Reserved (deprecated)", /* 1 */ + "Fixed hard disk", /* 2 */ + "Dynamic hard disk", /* 3 */ + "Differencing hard disk", /* 4 */ + "Reserved (deprecated)", /* 5 */ + "Reserved (deprecated)" /* 6 */ +}; + +#define HD_TYPE_MAX 6 + +struct prt_loc { + u32 code; /* Platform code -- see defines below. */ + u32 data_space; /* Number of 512-byte sectors to store locator */ + u32 data_len; /* Actual length of parent locator in bytes */ + u32 res; /* Must be zero */ + u64 data_offset; /* Absolute offset of locator data (bytes) */ +}; + +/* Platform Codes */ +#define PLAT_CODE_NONE 0x0 +#define PLAT_CODE_WI2R 0x57693272 /* deprecated */ +#define PLAT_CODE_WI2K 0x5769326B /* deprecated */ +#define PLAT_CODE_W2RU 0x57327275 /* Windows relative path (UTF-16) */ +#define PLAT_CODE_W2KU 0x57326B75 /* Windows absolute path (UTF-16) */ +#define PLAT_CODE_MAC 0x4D616320 /* MacOS alias stored as a blob. */ +#define PLAT_CODE_MACX 0x4D616358 /* File URL (UTF-8), see RFC 2396. */ + +/* ---------------------------------------------------------------------- */ +/* This is the dynamic disk header. */ +/* ---------------------------------------------------------------------- */ + +struct dd_hdr { + char cookie[8]; /* Should contain "cxsparse" */ + u64 data_offset; /* Byte offset of next record. (Unused) 0xffs */ + u64 table_offset; /* Absolute offset to the BAT. */ + u32 hdr_ver; /* Version of the dd_hdr (major,minor) */ + u32 max_bat_size; /* Maximum number of entries in the BAT */ + u32 block_size; /* Block size in bytes. Must be power of 2. */ + u32 checksum; /* Header checksum. 1's comp of all fields. */ + blk_uuid_t prt_uuid; /* ID of the parent disk. */ + u32 prt_ts; /* Modification time of the parent disk */ + u32 res1; /* Reserved. */ + char prt_name[512]; /* Parent unicode name. */ + struct prt_loc loc[8]; /* Parent locator entries. */ + char res2[256]; /* Reserved. */ +}; + +/* VHD cookie string. */ +static const char DD_COOKIE[9] = "cxsparse"; + +/* Version field in hd_ftr */ +#define DD_VERSION 0x00010000 + +/* Default blocksize is 2 meg. */ +#define DD_BLOCKSIZE_DEFAULT 0x00200000 + +#define DD_BLK_UNUSED 0xFFFFFFFF + +struct dd_batmap_hdr { + char cookie[8]; /* should contain "tdbatmap" */ + u64 batmap_offset; /* byte offset to batmap */ + u32 batmap_size; /* batmap size in sectors */ + u32 batmap_version; /* version of batmap */ + u32 checksum; /* batmap checksum -- 1's complement of batmap */ +}; + +static const char VHD_BATMAP_COOKIE[9] = "tdbatmap"; + +/* + * version 1.1: signed char checksum + */ +#define VHD_BATMAP_VERSION(major, minor) (((major) << 16) | ((minor) & 0x0000FFFF)) +#define VHD_BATMAP_CURRENT_VERSION VHD_BATMAP_VERSION(1, 2) + +/* Layout of a dynamic disk: + * + * +-------------------------------------------------+ + * | Mirror image of HD footer (hd_ftr) (512 bytes) | + * +-------------------------------------------------+ + * | Sparse drive header (dd_hdr) (1024 bytes) | + * +-------------------------------------------------+ + * | BAT (Block allocation table) | + * | - Array of absolute sector offsets into the | + * | file (u32). | + * | - Rounded up to a sector boundary. | + * | - Unused entries are marked as 0xFFFFFFFF | + * | - max entries in dd_hdr->max_bat_size | + * +-------------------------------------------------+ + * | Data Block 0 | + * | Bitmap (padded to 512 byte sector boundary) | + * | - each bit indicates whether the associated | + * | sector within this block is used. | + * | Data | + * | - power-of-two multiple of sectors. | + * | - default 2MB (4096 * 512) | + * | - Any entries with zero in bitmap should be | + * | zero on disk | + * +-------------------------------------------------+ + * | Data Block 1 | + * +-------------------------------------------------+ + * | ... | + * +-------------------------------------------------+ + * | Data Block n | + * +-------------------------------------------------+ + * | HD Footer (511 bytes) | + * +-------------------------------------------------+ + */ + +#endif diff --git a/tools/vhd-tools/lvm/Makefile b/tools/vhd-tools/lvm/Makefile new file mode 100644 index 00000000000..d5e07b2202d --- /dev/null +++ b/tools/vhd-tools/lvm/Makefile @@ -0,0 +1,38 @@ +XEN_ROOT = ../../.. +BLKTAP_ROOT := .. +include $(XEN_ROOT)/tools/Rules.mk + +ifeq ($(LVM_UTIL_TEST),y) +TEST := lvm-util +endif + +CFLAGS += -Werror +CFLAGS += -Wno-unused +CFLAGS += -I../include +CFLAGS += -D_GNU_SOURCE + +ifeq ($(CONFIG_X86_64),y) +CFLAGS += -fPIC +endif + +# Get gcc to generate the dependencies for us. +CFLAGS += -Wp,-MD,.$(@F).d +DEPS = .*.d + +LVM-OBJS := lvm-util.o + +all: build + +build: $(TEST) $(LVM-OBJS) + +install: all + +lvm-util: lvm-util.o + $(CC) $(CFLAGS) -DLVM_UTIL $(LDFLAGS) -o lvm-util lvm-util.c + +clean: + rm -rf *.o *~ $(DEPS) $(IBIN) + +.PHONY: all build clean install lvm-util + +-include $(DEPS) diff --git a/tools/vhd-tools/lvm/lvm-util.c b/tools/vhd-tools/lvm/lvm-util.c new file mode 100644 index 00000000000..b456e0438b1 --- /dev/null +++ b/tools/vhd-tools/lvm/lvm-util.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include + +#include "lvm-util.h" + +#define _NAME "%255s" +static char line[1024]; + +static inline int +lvm_read_line(FILE *scan) +{ + memset(line, 0, sizeof(line)); + return (fscanf(scan, "%1023[^\n]", line) != 1); +} + +static inline int +lvm_next_line(FILE *scan) +{ + return (fscanf(scan, "%1023[\n]", line) != 1); +} + +static int +lvm_copy_name(char *dst, const char *src, size_t size) +{ + if (strnlen(src, size) == size) + return -ENAMETOOLONG; + + strcpy(dst, src); + return 0; +} + +static int +lvm_parse_pv(struct vg *vg, const char *name, int pvs, uint64_t start) +{ + int i, err; + struct pv *pv; + + pv = NULL; + + if (!vg->pvs) { + vg->pvs = calloc(pvs, sizeof(struct pv)); + if (!vg->pvs) + return -ENOMEM; + } + + for (i = 0; i < pvs; i++) { + pv = vg->pvs + i; + + if (!pv->name[0]) + break; + + if (!strcmp(pv->name, name)) + return -EEXIST; + } + + if (!pv) + return -ENOENT; + + if (i == pvs) + return -ENOMEM; + + err = lvm_copy_name(pv->name, name, sizeof(pv->name) - 1); + if (err) + return err; + + pv->start = start; + return 0; +} + +static int +lvm_open_vg(const char *vgname, struct vg *vg) +{ + FILE *scan; + int i, err, pvs, lvs; + char *cmd, pvname[256]; + uint64_t size, pv_start; + + memset(vg, 0, sizeof(*vg)); + + err = asprintf(&cmd, "/usr/sbin/vgs %s --noheadings --nosuffix --units=b " + "--options=vg_name,vg_extent_size,lv_count,pv_count," + "pv_name,pe_start --unbuffered 2> /dev/null", vgname); + if (err == -1) + return -ENOMEM; + + errno = 0; + scan = popen(cmd, "r"); + if (!scan) { + err = (errno ? -errno : ENOMEM); + goto out; + } + + for (;;) { + if (lvm_read_line(scan)) + break; + + err = -EINVAL; + if (sscanf(line, _NAME" %"SCNu64" %d %d "_NAME" %"SCNu64, + vg->name, &size, &lvs, &pvs, pvname, &pv_start) != 6) + goto out; + + if (strcmp(vg->name, vgname)) + goto out; + + err = lvm_parse_pv(vg, pvname, pvs, pv_start); + if (err) + goto out; + + if (lvm_next_line(scan)) + break; + } + + err = -EINVAL; + if (strcmp(vg->name, vgname)) + goto out; + + for (i = 0; i < pvs; i++) + if (!vg->pvs[i].name[0]) + goto out; + + err = -ENOMEM; + vg->lvs = calloc(lvs, sizeof(struct lv)); + if (!vg->lvs) + goto out; + + err = 0; + vg->lv_cnt = lvs; + vg->pv_cnt = pvs; + vg->extent_size = size; + +out: + if (scan) + pclose(scan); + if (err) + lvm_free_vg(vg); + free(cmd); + return err; +} + +static int +lvm_parse_lv_devices(struct vg *vg, struct lv_segment *seg, char *devices) +{ + int i; + uint64_t start, pe_start; + + for (i = 0; i < strlen(devices); i++) + if (strchr(",()", devices[i])) + devices[i] = ' '; + + if (sscanf(devices, _NAME" %"SCNu64, seg->device, &start) != 2) + return -EINVAL; + + pe_start = -1; + for (i = 0; i < vg->pv_cnt; i++) + if (!strcmp(vg->pvs[i].name, seg->device)) { + pe_start = vg->pvs[i].start; + break; + } + + if (pe_start == -1) + return -EINVAL; + + seg->pe_start = (start * vg->extent_size) + pe_start; + return 0; +} + +static int +lvm_scan_lvs(struct vg *vg) +{ + char *cmd; + FILE *scan; + int i, err; + + err = asprintf(&cmd, "/usr/sbin/lvs %s --noheadings --nosuffix --units=b " + "--options=lv_name,lv_size,segtype,seg_count,seg_start," + "seg_size,devices --unbuffered 2> /dev/null", vg->name); + if (err == -1) + return -ENOMEM; + + errno = 0; + scan = popen(cmd, "r"); + if (!scan) { + err = (errno ? -errno : -ENOMEM); + goto out; + } + + for (i = 0;;) { + int segs; + struct lv *lv; + struct lv_segment seg; + uint64_t size, seg_start; + char type[32], name[256], dev[256], devices[1024]; + + if (i >= vg->lv_cnt) + break; + + if (lvm_read_line(scan)) { + vg->lv_cnt = i; + break; + } + + err = -EINVAL; + lv = vg->lvs + i; + + if (sscanf(line, _NAME" %"SCNu64" %31s %u %"SCNu64" %"SCNu64" %1023s", + name, &size, type, &segs, &seg_start, + &seg.pe_size, devices) != 7) + goto out; + + if (seg_start) + goto next; + + if (!strcmp(type, "linear")) + seg.type = LVM_SEG_TYPE_LINEAR; + else + seg.type = LVM_SEG_TYPE_UNKNOWN; + + if (lvm_parse_lv_devices(vg, &seg, devices)) + goto out; + + i++; + lv->size = size; + lv->segments = segs; + lv->first_segment = seg; + + err = lvm_copy_name(lv->name, name, sizeof(lv->name) - 1); + if (err) + goto out; + err = -EINVAL; + + next: + if (lvm_next_line(scan)) + goto out; + } + + err = 0; + +out: + if (scan) + pclose(scan); + free(cmd); + return err; +} + +void +lvm_free_vg(struct vg *vg) +{ + free(vg->lvs); + free(vg->pvs); + memset(vg, 0, sizeof(*vg)); +} + +int +lvm_scan_vg(const char *vg_name, struct vg *vg) +{ + int err; + + memset(vg, 0, sizeof(*vg)); + + err = lvm_open_vg(vg_name, vg); + if (err) + return err; + + err = lvm_scan_lvs(vg); + if (err) { + lvm_free_vg(vg); + return err; + } + + return 0; +} + +#ifdef LVM_UTIL +static int +usage(void) +{ + printf("usage: lvm-util \n"); + exit(EINVAL); +} + +int +main(int argc, char **argv) +{ + int i, err; + struct vg vg; + struct pv *pv; + struct lv *lv; + struct lv_segment *seg; + + if (argc != 2) + usage(); + + err = lvm_scan_vg(argv[1], &vg); + if (err) { + printf("scan failed: %d\n", err); + return (err >= 0 ? err : -err); + } + + + printf("vg %s: extent_size: %"PRIu64", pvs: %d, lvs: %d\n", + vg.name, vg.extent_size, vg.pv_cnt, vg.lv_cnt); + + for (i = 0; i < vg.pv_cnt; i++) { + pv = vg.pvs + i; + printf("pv %s: start %"PRIu64"\n", pv->name, pv->start); + } + + for (i = 0; i < vg.lv_cnt; i++) { + lv = vg.lvs + i; + seg = &lv->first_segment; + printf("lv %s: size: %"PRIu64", segments: %u, type: %u, " + "dev: %s, pe_start: %"PRIu64", pe_size: %"PRIu64"\n", + lv->name, lv->size, lv->segments, seg->type, + seg->device, seg->pe_start, seg->pe_size); + } + + lvm_free_vg(&vg); + return 0; +} +#endif diff --git a/tools/vhd-tools/vhd/Makefile b/tools/vhd-tools/vhd/Makefile new file mode 100644 index 00000000000..48a6e540b13 --- /dev/null +++ b/tools/vhd-tools/vhd/Makefile @@ -0,0 +1,56 @@ +XEN_ROOT=../../.. +BLKTAP_ROOT := .. +include $(XEN_ROOT)/tools/Rules.mk + +SUBDIRS-y := +SUBDIRS-y += lib + +IBIN = vhd-util vhd-update +INST_DIR = $(SBINDIR) + +CFLAGS += -Werror +CFLAGS += -Wno-unused +CFLAGS += -I../include +CFLAGS += -D_GNU_SOURCE + +ifeq ($(CONFIG_X86_64),y) +CFLAGS += -fPIC +endif + +ifeq ($(VHD_STATIC),y) +CFLAGS += -static +endif + +LIBS := -Llib -lvhd +ifeq ($(CONFIG_Linux),y) +LIBS += -luuid +endif + +# Get gcc to generate the dependencies for us. +CFLAGS += -Wp,-MD,.$(@F).d +DEPS = .*.d + +all: subdirs-all build + +build: $(IBIN) + +LIBS_DEPENDS := lib/libvhd.so lib/vhd.a +$(LIBS_DEPENDS):subdirs-all + +vhd-util: vhd-util.o $(LIBS_DEPENDS) + $(CC) $(CFLAGS) -o vhd-util vhd-util.o $(LDFLAGS) $(LIBS) + +vhd-update: vhd-update.o $(LIBS_DEPENDS) + $(CC) $(CFLAGS) -o vhd-update vhd-update.o $(LDFLAGS) $(LIBS) + +install: all + $(MAKE) subdirs-install + $(INSTALL_DIR) -p $(DESTDIR)$(INST_DIR) + $(INSTALL_PROG) $(IBIN) $(DESTDIR)$(INST_DIR) + +clean: subdirs-clean + rm -rf *.o *~ $(DEPS) $(IBIN) + +.PHONY: all build clean install vhd-util vhd-update + +-include $(DEPS) diff --git a/tools/vhd-tools/vhd/lib/Makefile b/tools/vhd-tools/vhd/lib/Makefile new file mode 100644 index 00000000000..fa5b417619d --- /dev/null +++ b/tools/vhd-tools/vhd/lib/Makefile @@ -0,0 +1,75 @@ +XEN_ROOT=../../../.. +BLKTAP_ROOT := ../.. +include $(XEN_ROOT)/tools/Rules.mk + +LIBVHD-MAJOR = 1.0 +LIBVHD-MINOR = 0 +LIBVHD-SONAME = libvhd.so.$(LIBVHD-MAJOR) + +LVM-UTIL-OBJ := $(BLKTAP_ROOT)/lvm/lvm-util.o + +LIBVHD-BUILD := libvhd.a + +INST-DIR = $(LIBDIR) + +CFLAGS += -Werror +CFLAGS += -Wno-unused +CFLAGS += -I../../include +CFLAGS += -D_GNU_SOURCE +CFLAGS += -fPIC +CFLAGS += -g + +ifeq ($(CONFIG_Linux),y) +LIBS := -luuid +endif + +# Get gcc to generate the dependencies for us. +CFLAGS += -Wp,-MD,.$(@F).d +DEPS = .*.d + +LIB-SRCS := libvhd.c +LIB-SRCS += libvhd-journal.c +LIB-SRCS += vhd-util-coalesce.c +LIB-SRCS += vhd-util-create.c +LIB-SRCS += vhd-util-fill.c +LIB-SRCS += vhd-util-modify.c +LIB-SRCS += vhd-util-query.c +LIB-SRCS += vhd-util-read.c +LIB-SRCS += vhd-util-repair.c +LIB-SRCS += vhd-util-resize.c +LIB-SRCS += vhd-util-revert.c +LIB-SRCS += vhd-util-set-field.c +LIB-SRCS += vhd-util-snapshot.c +LIB-SRCS += vhd-util-scan.c +LIB-SRCS += vhd-util-check.c +LIB-SRCS += relative-path.c +LIB-SRCS += atomicio.c + +LIB-OBJS = $(patsubst %.c,%.o,$(LIB-SRCS)) +LIB-OBJS += $(LVM-UTIL-OBJ) + +LIBVHD = libvhd.a libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) + +all: build + +build: $(LIBVHD-BUILD) + +libvhd.a: $(LIB-OBJS) + $(CC) $(CFLAGS) -Wl,$(SONAME_LDFLAG),$(LIBVHD-SONAME) $(SHLIB_CFLAGS) \ + $(LDFLAGS) -o libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) $(LIBS) $^ + ln -sf libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) libvhd.so.$(LIBVHD-MAJOR) + ln -sf libvhd.so.$(LIBVHD-MAJOR) libvhd.so + $(AR) rc $@ $^ + +install: all + $(INSTALL_DIR) -p $(DESTDIR)$(INST-DIR) + $(INSTALL_DATA) $(LIBVHD) $(DESTDIR)$(INST-DIR) + ln -sf libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) $(DESTDIR)$(INST-DIR)/libvhd.so.$(LIBVHD-MAJOR) + ln -sf libvhd.so.$(LIBVHD-MAJOR) $(DESTDIR)$(INST-DIR)/libvhd.so + +clean: + rm -rf *.a *.so* *.o *~ $(DEPS) $(LIBVHD) + +.PHONY: all build clean install libvhd + +-include $(DEPS) diff --git a/tools/vhd-tools/vhd/lib/atomicio.c b/tools/vhd-tools/vhd/lib/atomicio.c new file mode 100644 index 00000000000..ae0e24b00ad --- /dev/null +++ b/tools/vhd-tools/vhd/lib/atomicio.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved. + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * 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 ``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 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 +#include +#include "atomicio.h" + +/* + * ensure all of data on socket comes through. f==read || f==vwrite + */ +size_t +atomicio(f, fd, _s, n) + ssize_t (*f) (int, void *, size_t); + int fd; + void *_s; + size_t n; +{ + char *s = _s; + size_t pos = 0; + ssize_t res; + + while (n > pos) { + res = (f) (fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + return 0; + case 0: + errno = EPIPE; + return pos; + default: + pos += (size_t)res; + } + } + return (pos); +} + diff --git a/tools/vhd-tools/vhd/lib/libvhd-journal.c b/tools/vhd-tools/vhd/lib/libvhd-journal.c new file mode 100644 index 00000000000..24383ff62b2 --- /dev/null +++ b/tools/vhd-tools/vhd/lib/libvhd-journal.c @@ -0,0 +1,1534 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include +#include + +#include "atomicio.h" +#include "libvhd-journal.h" + +#define VHD_JOURNAL_ENTRY_TYPE_FOOTER_P 1 +#define VHD_JOURNAL_ENTRY_TYPE_FOOTER_C 2 +#define VHD_JOURNAL_ENTRY_TYPE_HEADER 3 +#define VHD_JOURNAL_ENTRY_TYPE_LOCATOR 4 +#define VHD_JOURNAL_ENTRY_TYPE_BAT 5 +#define VHD_JOURNAL_ENTRY_TYPE_BATMAP_H 6 +#define VHD_JOURNAL_ENTRY_TYPE_BATMAP_M 7 +#define VHD_JOURNAL_ENTRY_TYPE_DATA 8 + +typedef struct vhd_journal_entry { + uint64_t cookie; + uint32_t type; + uint32_t size; + uint64_t offset; + uint32_t checksum; +} vhd_journal_entry_t; + +static inline int +vhd_journal_seek(vhd_journal_t *j, off_t offset, int whence) +{ + off_t off; + + off = lseek(j->jfd, offset, whence); + if (off == (off_t)-1) + return -errno; + + return 0; +} + +static inline off_t +vhd_journal_position(vhd_journal_t *j) +{ + return lseek(j->jfd, 0, SEEK_CUR); +} + +static inline int +vhd_journal_read(vhd_journal_t *j, void *buf, size_t size) +{ + ssize_t ret; + + errno = 0; + + ret = atomicio(read, j->jfd, buf, size); + if (ret != size) + return (errno ? -errno : -EIO); + + return 0; +} + +static inline int +vhd_journal_write(vhd_journal_t *j, void *buf, size_t size) +{ + ssize_t ret; + + errno = 0; + + ret = atomicio(vwrite, j->jfd, buf, size); + if (ret != size) + return (errno ? -errno : -EIO); + + return 0; +} + +static inline int +vhd_journal_truncate(vhd_journal_t *j, off_t length) +{ + int err; + + err = ftruncate(j->jfd, length); + if (err == -1) + return -errno; + + return 0; +} + +static inline int +vhd_journal_sync(vhd_journal_t *j) +{ + int err; + + err = fdatasync(j->jfd); + if (err) + return -errno; + + return 0; +} + +static inline void +vhd_journal_header_in(vhd_journal_header_t *header) +{ + BE64_IN(&header->vhd_footer_offset); + BE32_IN(&header->journal_data_entries); + BE32_IN(&header->journal_metadata_entries); + BE64_IN(&header->journal_data_offset); + BE64_IN(&header->journal_metadata_offset); +} + +static inline void +vhd_journal_header_out(vhd_journal_header_t *header) +{ + BE64_OUT(&header->vhd_footer_offset); + BE32_OUT(&header->journal_data_entries); + BE32_OUT(&header->journal_metadata_entries); + BE64_OUT(&header->journal_data_offset); + BE64_OUT(&header->journal_metadata_offset); +} + +static int +vhd_journal_validate_header(vhd_journal_t *j, vhd_journal_header_t *header) +{ + int err; + off_t eof; + + if (memcmp(header->cookie, + VHD_JOURNAL_HEADER_COOKIE, sizeof(header->cookie))) + return -EINVAL; + + err = vhd_journal_seek(j, j->header.journal_eof, SEEK_SET); + if (err) + return err; + + eof = vhd_journal_position(j); + if (eof == (off_t)-1) + return -errno; + + if (j->header.journal_data_offset > j->header.journal_eof) + return -EINVAL; + + if (j->header.journal_metadata_offset > j->header.journal_eof) + return -EINVAL; + + return 0; +} + +static int +vhd_journal_read_journal_header(vhd_journal_t *j, vhd_journal_header_t *header) +{ + int err; + size_t size; + + size = sizeof(vhd_journal_header_t); + err = vhd_journal_seek(j, 0, SEEK_SET); + if (err) + return err; + + err = vhd_journal_read(j, header, size); + if (err) + return err; + + vhd_journal_header_in(header); + + return vhd_journal_validate_header(j, header); +} + +static int +vhd_journal_write_header(vhd_journal_t *j, vhd_journal_header_t *header) +{ + int err; + size_t size; + vhd_journal_header_t h; + + memcpy(&h, header, sizeof(vhd_journal_header_t)); + + err = vhd_journal_validate_header(j, &h); + if (err) + return err; + + vhd_journal_header_out(&h); + size = sizeof(vhd_journal_header_t); + + err = vhd_journal_seek(j, 0, SEEK_SET); + if (err) + return err; + + err = vhd_journal_write(j, &h, size); + if (err) + return err; + + return 0; +} + +static int +vhd_journal_add_journal_header(vhd_journal_t *j) +{ + int err; + off_t off; + vhd_context_t *vhd; + + vhd = &j->vhd; + memset(&j->header, 0, sizeof(vhd_journal_header_t)); + + err = vhd_seek(vhd, 0, SEEK_END); + if (err) + return err; + + off = vhd_position(vhd); + if (off == (off_t)-1) + return -errno; + + err = vhd_get_footer(vhd); + if (err) + return err; + + blk_uuid_copy(&j->header.uuid, &vhd->footer.uuid); + memcpy(j->header.cookie, + VHD_JOURNAL_HEADER_COOKIE, sizeof(j->header.cookie)); + j->header.vhd_footer_offset = off - sizeof(vhd_footer_t); + j->header.journal_eof = sizeof(vhd_journal_header_t); + + return vhd_journal_write_header(j, &j->header); +} + +static void +vhd_journal_entry_in(vhd_journal_entry_t *entry) +{ + BE32_IN(&entry->type); + BE32_IN(&entry->size); + BE64_IN(&entry->offset); + BE64_IN(&entry->cookie); + BE32_IN(&entry->checksum); +} + +static void +vhd_journal_entry_out(vhd_journal_entry_t *entry) +{ + BE32_OUT(&entry->type); + BE32_OUT(&entry->size); + BE64_OUT(&entry->offset); + BE64_OUT(&entry->cookie); + BE32_OUT(&entry->checksum); +} + +static uint32_t +vhd_journal_checksum_entry(vhd_journal_entry_t *entry, char *buf, size_t size) +{ + int i; + unsigned char *blob; + uint32_t checksum, tmp; + + checksum = 0; + tmp = entry->checksum; + entry->checksum = 0; + + blob = (unsigned char *)entry; + for (i = 0; i < sizeof(vhd_journal_entry_t); i++) + checksum += blob[i]; + + blob = (unsigned char *)buf; + for (i = 0; i < size; i++) + checksum += blob[i]; + + entry->checksum = tmp; + return ~checksum; +} + +static int +vhd_journal_validate_entry(vhd_journal_entry_t *entry) +{ + if (entry->size == 0) + return -EINVAL; + + if (entry->size & (VHD_SECTOR_SIZE - 1)) + return -EINVAL; + + if (entry->cookie != VHD_JOURNAL_ENTRY_COOKIE) + return -EINVAL; + + return 0; +} + +static int +vhd_journal_read_entry(vhd_journal_t *j, vhd_journal_entry_t *entry) +{ + int err; + + err = vhd_journal_read(j, entry, sizeof(vhd_journal_entry_t)); + if (err) + return err; + + vhd_journal_entry_in(entry); + return vhd_journal_validate_entry(entry); +} + +static int +vhd_journal_write_entry(vhd_journal_t *j, vhd_journal_entry_t *entry) +{ + int err; + vhd_journal_entry_t e; + + err = vhd_journal_validate_entry(entry); + if (err) + return err; + + memcpy(&e, entry, sizeof(vhd_journal_entry_t)); + vhd_journal_entry_out(&e); + + err = vhd_journal_write(j, &e, sizeof(vhd_journal_entry_t)); + if (err) + err; + + return 0; +} + +static int +vhd_journal_validate_entry_data(vhd_journal_entry_t *entry, char *buf) +{ + int err; + uint32_t checksum; + + err = 0; + checksum = vhd_journal_checksum_entry(entry, buf, entry->size); + + if (checksum != entry->checksum) + return -EINVAL; + + return err; +} + +static int +vhd_journal_update(vhd_journal_t *j, off_t offset, + char *buf, size_t size, uint32_t type) +{ + int err; + off_t eof; + uint64_t *off, off_bak; + uint32_t *entries; + vhd_journal_entry_t entry; + + entry.type = type; + entry.size = size; + entry.offset = offset; + entry.cookie = VHD_JOURNAL_ENTRY_COOKIE; + entry.checksum = vhd_journal_checksum_entry(&entry, buf, size); + + err = vhd_journal_seek(j, j->header.journal_eof, SEEK_SET); + if (err) + return err; + + err = vhd_journal_write_entry(j, &entry); + if (err) + goto fail; + + err = vhd_journal_write(j, buf, size); + if (err) + goto fail; + + if (type == VHD_JOURNAL_ENTRY_TYPE_DATA) { + off = &j->header.journal_data_offset; + entries = &j->header.journal_data_entries; + } else { + off = &j->header.journal_metadata_offset; + entries = &j->header.journal_metadata_entries; + } + + off_bak = *off; + if (!(*entries)++) + *off = j->header.journal_eof; + j->header.journal_eof += (size + sizeof(vhd_journal_entry_t)); + + err = vhd_journal_write_header(j, &j->header); + if (err) { + if (!--(*entries)) + *off = off_bak; + j->header.journal_eof -= (size + sizeof(vhd_journal_entry_t)); + goto fail; + } + + return 0; + +fail: + if (!j->is_block) + vhd_journal_truncate(j, j->header.journal_eof); + return err; +} + +static int +vhd_journal_add_footer(vhd_journal_t *j) +{ + int err; + off_t off; + vhd_context_t *vhd; + vhd_footer_t footer; + + vhd = &j->vhd; + + err = vhd_seek(vhd, 0, SEEK_END); + if (err) + return err; + + off = vhd_position(vhd); + if (off == (off_t)-1) + return -errno; + + err = vhd_read_footer_at(vhd, &footer, off - sizeof(vhd_footer_t)); + if (err) + return err; + + vhd_footer_out(&footer); + err = vhd_journal_update(j, off - sizeof(vhd_footer_t), + (char *)&footer, + sizeof(vhd_footer_t), + VHD_JOURNAL_ENTRY_TYPE_FOOTER_P); + if (err) + return err; + + if (!vhd_type_dynamic(vhd)) + return 0; + + err = vhd_read_footer_at(vhd, &footer, 0); + if (err) + return err; + + vhd_footer_out(&footer); + err = vhd_journal_update(j, 0, + (char *)&footer, + sizeof(vhd_footer_t), + VHD_JOURNAL_ENTRY_TYPE_FOOTER_C); + + return err; +} + +static int +vhd_journal_add_header(vhd_journal_t *j) +{ + int err; + off_t off; + vhd_context_t *vhd; + vhd_header_t header; + + vhd = &j->vhd; + + err = vhd_read_header(vhd, &header); + if (err) + return err; + + off = vhd->footer.data_offset; + + vhd_header_out(&header); + err = vhd_journal_update(j, off, + (char *)&header, + sizeof(vhd_header_t), + VHD_JOURNAL_ENTRY_TYPE_HEADER); + + return err; +} + +static int +vhd_journal_add_locators(vhd_journal_t *j) +{ + int i, n, err; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_get_header(vhd); + if (err) + return err; + + n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t); + for (i = 0; i < n; i++) { + char *buf; + off_t off; + size_t size; + vhd_parent_locator_t *loc; + + loc = vhd->header.loc + i; + err = vhd_validate_platform_code(loc->code); + if (err) + return err; + + if (loc->code == PLAT_CODE_NONE) + continue; + + off = loc->data_offset; + size = vhd_parent_locator_size(loc); + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) + return -err; + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + goto end; + + err = vhd_read(vhd, buf, size); + if (err) + goto end; + + err = vhd_journal_update(j, off, buf, size, + VHD_JOURNAL_ENTRY_TYPE_LOCATOR); + if (err) + goto end; + + err = 0; + + end: + free(buf); + if (err) + break; + } + + return err; +} + +static int +vhd_journal_add_bat(vhd_journal_t *j) +{ + int err; + off_t off; + size_t size; + vhd_bat_t bat; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_get_header(vhd); + if (err) + return err; + + err = vhd_read_bat(vhd, &bat); + if (err) + return err; + + off = vhd->header.table_offset; + size = vhd_bytes_padded(bat.entries * sizeof(uint32_t)); + + vhd_bat_out(&bat); + err = vhd_journal_update(j, off, (char *)bat.bat, size, + VHD_JOURNAL_ENTRY_TYPE_BAT); + + free(bat.bat); + return err; +} + +static int +vhd_journal_add_batmap(vhd_journal_t *j) +{ + int err; + off_t off; + size_t size; + vhd_context_t *vhd; + vhd_batmap_t batmap; + + vhd = &j->vhd; + + err = vhd_batmap_header_offset(vhd, &off); + if (err) + return err; + + err = vhd_read_batmap(vhd, &batmap); + if (err) + return err; + + size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr)); + + vhd_batmap_header_out(&batmap); + err = vhd_journal_update(j, off, (char *)&batmap.header, size, + VHD_JOURNAL_ENTRY_TYPE_BATMAP_H); + if (err) + goto out; + + vhd_batmap_header_in(&batmap); + off = batmap.header.batmap_offset; + size = vhd_sectors_to_bytes(batmap.header.batmap_size); + + err = vhd_journal_update(j, off, batmap.map, size, + VHD_JOURNAL_ENTRY_TYPE_BATMAP_M); + +out: + free(batmap.map); + return err; +} + +static int +vhd_journal_add_metadata(vhd_journal_t *j) +{ + int err; + off_t eof; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_journal_add_footer(j); + if (err) + return err; + + if (!vhd_type_dynamic(vhd)) + return 0; + + err = vhd_journal_add_header(j); + if (err) + return err; + + err = vhd_journal_add_locators(j); + if (err) + return err; + + err = vhd_journal_add_bat(j); + if (err) + return err; + + if (vhd_has_batmap(vhd)) { + err = vhd_journal_add_batmap(j); + if (err) + return err; + } + + j->header.journal_data_offset = j->header.journal_eof; + return vhd_journal_write_header(j, &j->header); +} + +static int +__vhd_journal_read_footer(vhd_journal_t *j, + vhd_footer_t *footer, uint32_t type) +{ + int err; + vhd_journal_entry_t entry; + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != type) + return -EINVAL; + + if (entry.size != sizeof(vhd_footer_t)) + return -EINVAL; + + err = vhd_journal_read(j, footer, entry.size); + if (err) + return err; + + vhd_footer_in(footer); + return vhd_validate_footer(footer); +} + +static int +vhd_journal_read_footer(vhd_journal_t *j, vhd_footer_t *footer) +{ + return __vhd_journal_read_footer(j, footer, + VHD_JOURNAL_ENTRY_TYPE_FOOTER_P); +} + +static int +vhd_journal_read_footer_copy(vhd_journal_t *j, vhd_footer_t *footer) +{ + return __vhd_journal_read_footer(j, footer, + VHD_JOURNAL_ENTRY_TYPE_FOOTER_C); +} + +static int +vhd_journal_read_header(vhd_journal_t *j, vhd_header_t *header) +{ + int err; + vhd_journal_entry_t entry; + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != VHD_JOURNAL_ENTRY_TYPE_HEADER) + return -EINVAL; + + if (entry.size != sizeof(vhd_header_t)) + return -EINVAL; + + err = vhd_journal_read(j, header, entry.size); + if (err) + return err; + + vhd_header_in(header); + return vhd_validate_header(header); +} + +static int +vhd_journal_read_locators(vhd_journal_t *j, char ***locators, int *locs) +{ + int err, n, _locs; + char **_locators, *buf; + off_t pos; + vhd_journal_entry_t entry; + + _locs = 0; + *locs = 0; + *locators = NULL; + + n = sizeof(j->vhd.header.loc) / sizeof(vhd_parent_locator_t); + _locators = calloc(n, sizeof(char *)); + if (!_locators) + return -ENOMEM; + + for (;;) { + buf = NULL; + + pos = vhd_journal_position(j); + err = vhd_journal_read_entry(j, &entry); + if (err) + goto fail; + + if (entry.type != VHD_JOURNAL_ENTRY_TYPE_LOCATOR) { + err = vhd_journal_seek(j, pos, SEEK_SET); + if (err) + goto fail; + break; + } + + if (_locs >= n) { + err = -EINVAL; + goto fail; + } + + err = posix_memalign((void **)&buf, + VHD_SECTOR_SIZE, entry.size); + if (err) { + err = -err; + buf = NULL; + goto fail; + } + + err = vhd_journal_read(j, buf, entry.size); + if (err) + goto fail; + + _locators[_locs++] = buf; + err = 0; + } + + + *locs = _locs; + *locators = _locators; + + return 0; + +fail: + if (_locators) { + for (n = 0; n < _locs; n++) + free(_locators[n]); + free(_locators); + } + return err; +} + +static int +vhd_journal_read_bat(vhd_journal_t *j, vhd_bat_t *bat) +{ + int err; + size_t size; + vhd_context_t *vhd; + vhd_journal_entry_t entry; + + vhd = &j->vhd; + + size = vhd_bytes_padded(vhd->header.max_bat_size * sizeof(uint32_t)); + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BAT) + return -EINVAL; + + if (entry.size != size) + return -EINVAL; + + if (entry.offset != vhd->header.table_offset) + return -EINVAL; + + err = posix_memalign((void **)&bat->bat, VHD_SECTOR_SIZE, size); + if (err) + return -err; + + err = vhd_journal_read(j, bat->bat, entry.size); + if (err) + goto fail; + + bat->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT; + bat->entries = vhd->header.max_bat_size; + vhd_bat_in(bat); + + return 0; + +fail: + free(bat->bat); + bat->bat = NULL; + return err; +} + +static int +vhd_journal_read_batmap_header(vhd_journal_t *j, vhd_batmap_t *batmap) +{ + int err; + char *buf; + size_t size; + vhd_journal_entry_t entry; + + size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr)); + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BATMAP_H) + return -EINVAL; + + if (entry.size != size) + return -EINVAL; + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) + return err; + + err = vhd_journal_read(j, buf, entry.size); + if (err) { + free(buf); + return err; + } + + memcpy(&batmap->header, buf, sizeof(batmap->header)); + + vhd_batmap_header_in(batmap); + return vhd_validate_batmap_header(batmap); +} + +static int +vhd_journal_read_batmap_map(vhd_journal_t *j, vhd_batmap_t *batmap) +{ + int err; + vhd_journal_entry_t entry; + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BATMAP_M) + return -EINVAL; + + if (entry.size != vhd_sectors_to_bytes(batmap->header.batmap_size)) + return -EINVAL; + + if (entry.offset != batmap->header.batmap_offset) + return -EINVAL; + + err = posix_memalign((void **)&batmap->map, + VHD_SECTOR_SIZE, entry.size); + if (err) + return -err; + + err = vhd_journal_read(j, batmap->map, entry.size); + if (err) { + free(batmap->map); + batmap->map = NULL; + return err; + } + + return 0; +} + +static int +vhd_journal_read_batmap(vhd_journal_t *j, vhd_batmap_t *batmap) +{ + int err; + + err = vhd_journal_read_batmap_header(j, batmap); + if (err) + return err; + + err = vhd_journal_read_batmap_map(j, batmap); + if (err) + return err; + + err = vhd_validate_batmap(batmap); + if (err) { + free(batmap->map); + batmap->map = NULL; + return err; + } + + return 0; +} + +static int +vhd_journal_restore_footer(vhd_journal_t *j, vhd_footer_t *footer) +{ + return vhd_write_footer_at(&j->vhd, footer, + j->header.vhd_footer_offset); +} + +static int +vhd_journal_restore_footer_copy(vhd_journal_t *j, vhd_footer_t *footer) +{ + return vhd_write_footer_at(&j->vhd, footer, 0); +} + +static int +vhd_journal_restore_header(vhd_journal_t *j, vhd_header_t *header) +{ + off_t off; + vhd_context_t *vhd; + + vhd = &j->vhd; + off = vhd->footer.data_offset; + + return vhd_write_header_at(&j->vhd, header, off); +} + +static int +vhd_journal_restore_locators(vhd_journal_t *j, char **locators, int locs) +{ + size_t size; + vhd_context_t *vhd; + int i, n, lidx, err; + vhd_parent_locator_t *loc; + + lidx = 0; + vhd = &j->vhd; + + n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t); + + for (i = 0; i < n && lidx < locs; i++) { + loc = vhd->header.loc + i; + if (loc->code == PLAT_CODE_NONE) + continue; + + err = vhd_seek(vhd, loc->data_offset, SEEK_SET); + if (err) + return err; + + size = vhd_parent_locator_size(loc); + err = vhd_write(vhd, locators[lidx++], size); + if (err) + return err; + } + + return 0; +} + +static int +vhd_journal_restore_bat(vhd_journal_t *j, vhd_bat_t *bat) +{ + return vhd_write_bat(&j->vhd, bat); +} + +static int +vhd_journal_restore_batmap(vhd_journal_t *j, vhd_batmap_t *batmap) +{ + return vhd_write_batmap(&j->vhd, batmap); +} + +static int +vhd_journal_restore_metadata(vhd_journal_t *j) +{ + off_t off; + char **locators; + vhd_footer_t copy; + vhd_context_t *vhd; + int i, locs, hlocs, err; + + vhd = &j->vhd; + locs = 0; + hlocs = 0; + locators = NULL; + + err = vhd_journal_seek(j, sizeof(vhd_journal_header_t), SEEK_SET); + if (err) + return err; + + err = vhd_journal_read_footer(j, &vhd->footer); + if (err) + return err; + + if (!vhd_type_dynamic(vhd)) + goto restore; + + err = vhd_journal_read_footer_copy(j, ©); + if (err) + return err; + + err = vhd_journal_read_header(j, &vhd->header); + if (err) + return err; + + for (hlocs = 0, i = 0; i < vhd_parent_locator_count(vhd); i++) { + if (vhd_validate_platform_code(vhd->header.loc[i].code)) + return err; + + if (vhd->header.loc[i].code != PLAT_CODE_NONE) + hlocs++; + } + + if (hlocs) { + err = vhd_journal_read_locators(j, &locators, &locs); + if (err) + return err; + + if (hlocs != locs) { + err = -EINVAL; + goto out; + } + } + + err = vhd_journal_read_bat(j, &vhd->bat); + if (err) + goto out; + + if (vhd_has_batmap(vhd)) { + err = vhd_journal_read_batmap(j, &vhd->batmap); + if (err) + goto out; + } + +restore: + off = vhd_journal_position(j); + if (off == (off_t)-1) + return -errno; + + if (j->header.journal_data_offset != off) + return -EINVAL; + + err = vhd_journal_restore_footer(j, &vhd->footer); + if (err) + goto out; + + if (!vhd_type_dynamic(vhd)) + goto out; + + err = vhd_journal_restore_footer_copy(j, ©); + if (err) + goto out; + + err = vhd_journal_restore_header(j, &vhd->header); + if (err) + goto out; + + if (locs) { + err = vhd_journal_restore_locators(j, locators, locs); + if (err) + goto out; + } + + err = vhd_journal_restore_bat(j, &vhd->bat); + if (err) + goto out; + + if (vhd_has_batmap(vhd)) { + err = vhd_journal_restore_batmap(j, &vhd->batmap); + if (err) + goto out; + } + + err = 0; + +out: + if (locators) { + for (i = 0; i < locs; i++) + free(locators[i]); + free(locators); + } + + if (!err && !vhd->is_block) + err = ftruncate(vhd->fd, + j->header.vhd_footer_offset + + sizeof(vhd_footer_t)); + + return err; +} + +static int +vhd_journal_disable_vhd(vhd_journal_t *j) +{ + int err; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_get_footer(vhd); + if (err) + return err; + + memcpy(&vhd->footer.cookie, + VHD_POISON_COOKIE, sizeof(vhd->footer.cookie)); + vhd->footer.checksum = vhd_checksum_footer(&vhd->footer); + + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + return err; + + return 0; +} + +static int +vhd_journal_enable_vhd(vhd_journal_t *j) +{ + int err; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_get_footer(vhd); + if (err) + return err; + + if (!vhd_disabled(vhd)) + return 0; + + memcpy(&vhd->footer.cookie, HD_COOKIE, sizeof(vhd->footer.cookie)); + vhd->footer.checksum = vhd_checksum_footer(&vhd->footer); + + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + return err; + + return 0; +} + +int +vhd_journal_close(vhd_journal_t *j) +{ + if (j->jfd) + close(j->jfd); + + vhd_close(&j->vhd); + free(j->jname); + + return 0; +} + +int +vhd_journal_remove(vhd_journal_t *j) +{ + int err; + + err = vhd_journal_enable_vhd(j); + if (err) + return err; + + if (j->jfd) { + close(j->jfd); + if (!j->is_block) + unlink(j->jname); + } + + vhd_close(&j->vhd); + free(j->jname); + + return 0; +} + +int +vhd_journal_open(vhd_journal_t *j, const char *file, const char *jfile) +{ + int err; + vhd_context_t *vhd; + + memset(j, 0, sizeof(vhd_journal_t)); + + j->jfd = -1; + vhd = &j->vhd; + + j->jname = strdup(jfile); + if (j->jname == NULL) + return -ENOMEM; + + j->jfd = open(j->jname, O_LARGEFILE | O_RDWR); + if (j->jfd == -1) { + err = -errno; + goto fail; + } + + err = vhd_test_file_fixed(j->jname, &j->is_block); + if (err) + goto fail; + + vhd->fd = open(file, O_LARGEFILE | O_RDWR | O_DIRECT); + if (vhd->fd == -1) { + err = -errno; + goto fail; + } + + err = vhd_test_file_fixed(file, &vhd->is_block); + if (err) + goto fail; + + err = vhd_journal_read_journal_header(j, &j->header); + if (err) + goto fail; + + err = vhd_journal_restore_metadata(j); + if (err) + goto fail; + + close(vhd->fd); + free(vhd->bat.bat); + free(vhd->batmap.map); + + err = vhd_open(vhd, file, VHD_OPEN_RDWR); + if (err) + goto fail; + + err = vhd_get_bat(vhd); + if (err) + goto fail; + + if (vhd_has_batmap(vhd)) { + err = vhd_get_batmap(vhd); + if (err) + goto fail; + } + + err = vhd_journal_disable_vhd(j); + if (err) + goto fail; + + return 0; + +fail: + vhd_journal_close(j); + return err; +} + +int +vhd_journal_create(vhd_journal_t *j, const char *file, const char *jfile) +{ + char *buf; + int i, err; + size_t size; + off_t off; + struct stat stats; + + memset(j, 0, sizeof(vhd_journal_t)); + j->jfd = -1; + + j->jname = strdup(jfile); + if (j->jname == NULL) { + err = -ENOMEM; + goto fail1; + } + + if (access(j->jname, F_OK) == 0) { + err = vhd_test_file_fixed(j->jname, &j->is_block); + if (err) + goto fail1; + + if (!j->is_block) { + err = -EEXIST; + goto fail1; + } + } + + if (j->is_block) + j->jfd = open(j->jname, O_LARGEFILE | O_RDWR, 0644); + else + j->jfd = open(j->jname, + O_CREAT | O_TRUNC | O_LARGEFILE | O_RDWR, 0644); + if (j->jfd == -1) { + err = -errno; + goto fail1; + } + + err = vhd_open(&j->vhd, file, VHD_OPEN_RDWR | VHD_OPEN_STRICT); + if (err) + goto fail1; + + err = vhd_get_bat(&j->vhd); + if (err) + goto fail2; + + if (vhd_has_batmap(&j->vhd)) { + err = vhd_get_batmap(&j->vhd); + if (err) + goto fail2; + } + + err = vhd_journal_add_journal_header(j); + if (err) + goto fail2; + + err = vhd_journal_add_metadata(j); + if (err) + goto fail2; + + err = vhd_journal_disable_vhd(j); + if (err) + goto fail2; + + err = vhd_journal_sync(j); + if (err) + goto fail2; + + return 0; + +fail1: + if (j->jfd != -1) { + close(j->jfd); + if (!j->is_block) + unlink(j->jname); + } + free(j->jname); + memset(j, 0, sizeof(vhd_journal_t)); + + return err; + +fail2: + vhd_journal_remove(j); + return err; +} + +int +vhd_journal_add_block(vhd_journal_t *j, uint32_t block, char mode) +{ + int err; + char *buf; + off_t off; + size_t size; + uint64_t blk; + vhd_context_t *vhd; + + buf = NULL; + vhd = &j->vhd; + + if (!vhd_type_dynamic(vhd)) + return -EINVAL; + + err = vhd_get_bat(vhd); + if (err) + return err; + + if (block >= vhd->bat.entries) + return -ERANGE; + + blk = vhd->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return 0; + + off = vhd_sectors_to_bytes(blk); + + if (mode & VHD_JOURNAL_METADATA) { + size = vhd_sectors_to_bytes(vhd->bm_secs); + + err = vhd_read_bitmap(vhd, block, &buf); + if (err) + return err; + + err = vhd_journal_update(j, off, buf, size, + VHD_JOURNAL_ENTRY_TYPE_DATA); + + free(buf); + + if (err) + return err; + } + + if (mode & VHD_JOURNAL_DATA) { + off += vhd_sectors_to_bytes(vhd->bm_secs); + size = vhd_sectors_to_bytes(vhd->spb); + + err = vhd_read_block(vhd, block, &buf); + if (err) + return err; + + err = vhd_journal_update(j, off, buf, size, + VHD_JOURNAL_ENTRY_TYPE_DATA); + free(buf); + + if (err) + return err; + } + + return vhd_journal_sync(j); +} + +/* + * commit indicates the transaction completed + * successfully and we can remove the undo log + */ +int +vhd_journal_commit(vhd_journal_t *j) +{ + int err; + + j->header.journal_data_entries = 0; + j->header.journal_metadata_entries = 0; + j->header.journal_data_offset = 0; + j->header.journal_metadata_offset = 0; + + err = vhd_journal_write_header(j, &j->header); + if (err) + return err; + + if (!j->is_block) + err = vhd_journal_truncate(j, sizeof(vhd_journal_header_t)); + if (err) + return -errno; + + return 0; +} + +/* + * revert indicates the transaction failed + * and we should revert any changes via the undo log + */ +int +vhd_journal_revert(vhd_journal_t *j) +{ + int i, err; + char *buf, *file; + vhd_context_t *vhd; + vhd_journal_entry_t entry; + + err = 0; + vhd = &j->vhd; + buf = NULL; + + file = strdup(vhd->file); + if (!file) + return -ENOMEM; + + vhd_close(&j->vhd); + j->vhd.fd = open(file, O_RDWR | O_DIRECT | O_LARGEFILE); + if (j->vhd.fd == -1) { + free(file); + return -errno; + } + + err = vhd_test_file_fixed(file, &vhd->is_block); + if (err) { + free(file); + return err; + } + + err = vhd_journal_restore_metadata(j); + if (err) { + free(file); + return err; + } + + close(vhd->fd); + free(vhd->bat.bat); + free(vhd->batmap.map); + + err = vhd_open(vhd, file, VHD_OPEN_RDWR); + free(file); + if (err) + return err; + + err = vhd_journal_seek(j, j->header.journal_data_offset, SEEK_SET); + if (err) + return err; + + for (i = 0; i < j->header.journal_data_entries; i++) { + err = vhd_journal_read_entry(j, &entry); + if (err) + goto end; + + err = posix_memalign((void **)&buf, + VHD_SECTOR_SIZE, entry.size); + if (err) { + err = -err; + buf = NULL; + goto end; + } + + err = vhd_journal_read(j, buf, entry.size); + if (err) + goto end; + + err = vhd_journal_validate_entry_data(&entry, buf); + if (err) + goto end; + + err = vhd_seek(vhd, entry.offset, SEEK_SET); + if (err) + goto end; + + err = vhd_write(vhd, buf, entry.size); + if (err) + goto end; + + err = 0; + + end: + free(buf); + buf = NULL; + if (err) + break; + } + + if (err) + return err; + + if (!vhd->is_block) { + err = ftruncate(vhd->fd, j->header.vhd_footer_offset + + sizeof(vhd_footer_t)); + if (err) + return -errno; + } + + return vhd_journal_sync(j); +} diff --git a/tools/vhd-tools/vhd/lib/libvhd.c b/tools/vhd-tools/vhd/lib/libvhd.c new file mode 100644 index 00000000000..bc9649a6396 --- /dev/null +++ b/tools/vhd-tools/vhd/lib/libvhd.c @@ -0,0 +1,3352 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. +*/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libvhd.h" +#include "relative-path.h" + +static int libvhd_dbg = 0; + +void +libvhd_set_log_level(int level) +{ + if (level) + libvhd_dbg = 1; +} + +#define VHDLOG(_f, _a...) \ + do { \ + if (libvhd_dbg) \ + syslog(LOG_INFO, "libvhd::%s: "_f, \ + __func__, ##_a); \ + } while (0) + +#define BIT_MASK 0x80 + +#ifdef ENABLE_FAILURE_TESTING +const char* ENV_VAR_FAIL[NUM_FAIL_TESTS] = { + "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN", + "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR", + "VHD_UTIL_TEST_FAIL_REPARENT_END", + "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN", + "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED", + "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED", + "VHD_UTIL_TEST_FAIL_RESIZE_END" +}; +int TEST_FAIL[NUM_FAIL_TESTS]; +#endif // ENABLE_FAILURE_TESTING + +static inline int +test_bit (volatile char *addr, int nr) +{ + return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0; +} + +static inline void +set_bit (volatile char *addr, int nr) +{ + addr[nr >> 3] |= (BIT_MASK >> (nr & 7)); +} + +static inline void +clear_bit (volatile char *addr, int nr) +{ + addr[nr >> 3] &= ~(BIT_MASK >> (nr & 7)); +} + +static inline int +old_test_bit(volatile char *addr, int nr) +{ + return (((uint32_t *)addr)[nr >> 5] >> (nr & 31)) & 1; +} + +static inline void +old_set_bit(volatile char *addr, int nr) +{ + ((uint32_t *)addr)[nr >> 5] |= (1 << (nr & 31)); +} + +static inline void +old_clear_bit(volatile char *addr, int nr) +{ + ((uint32_t *)addr)[nr >> 5] &= ~(1 << (nr & 31)); +} + +void +vhd_footer_in(vhd_footer_t *footer) +{ + BE32_IN(&footer->features); + BE32_IN(&footer->ff_version); + BE64_IN(&footer->data_offset); + BE32_IN(&footer->timestamp); + BE32_IN(&footer->crtr_ver); + BE32_IN(&footer->crtr_os); + BE64_IN(&footer->orig_size); + BE64_IN(&footer->curr_size); + BE32_IN(&footer->geometry); + BE32_IN(&footer->type); + BE32_IN(&footer->checksum); +} + +void +vhd_footer_out(vhd_footer_t *footer) +{ + BE32_OUT(&footer->features); + BE32_OUT(&footer->ff_version); + BE64_OUT(&footer->data_offset); + BE32_OUT(&footer->timestamp); + BE32_OUT(&footer->crtr_ver); + BE32_OUT(&footer->crtr_os); + BE64_OUT(&footer->orig_size); + BE64_OUT(&footer->curr_size); + BE32_OUT(&footer->geometry); + BE32_OUT(&footer->type); + BE32_OUT(&footer->checksum); +} + +void +vhd_header_in(vhd_header_t *header) +{ + int i, n; + + BE64_IN(&header->data_offset); + BE64_IN(&header->table_offset); + BE32_IN(&header->hdr_ver); + BE32_IN(&header->max_bat_size); + BE32_IN(&header->block_size); + BE32_IN(&header->checksum); + BE32_IN(&header->prt_ts); + + n = sizeof(header->loc) / sizeof(vhd_parent_locator_t); + + for (i = 0; i < n; i++) { + BE32_IN(&header->loc[i].code); + BE32_IN(&header->loc[i].data_space); + BE32_IN(&header->loc[i].data_len); + BE64_IN(&header->loc[i].data_offset); + } +} + +void +vhd_header_out(vhd_header_t *header) +{ + int i, n; + + BE64_OUT(&header->data_offset); + BE64_OUT(&header->table_offset); + BE32_OUT(&header->hdr_ver); + BE32_OUT(&header->max_bat_size); + BE32_OUT(&header->block_size); + BE32_OUT(&header->checksum); + BE32_OUT(&header->prt_ts); + + n = sizeof(header->loc) / sizeof(vhd_parent_locator_t); + + for (i = 0; i < n; i++) { + BE32_OUT(&header->loc[i].code); + BE32_OUT(&header->loc[i].data_space); + BE32_OUT(&header->loc[i].data_len); + BE64_OUT(&header->loc[i].data_offset); + } +} + +void +vhd_batmap_header_in(vhd_batmap_t *batmap) +{ + BE64_IN(&batmap->header.batmap_offset); + BE32_IN(&batmap->header.batmap_size); + BE32_IN(&batmap->header.batmap_version); + BE32_IN(&batmap->header.checksum); +} + +void +vhd_batmap_header_out(vhd_batmap_t *batmap) +{ + BE64_OUT(&batmap->header.batmap_offset); + BE32_OUT(&batmap->header.batmap_size); + BE32_OUT(&batmap->header.batmap_version); + BE32_OUT(&batmap->header.checksum); +} + +void +vhd_bat_in(vhd_bat_t *bat) +{ + int i; + + for (i = 0; i < bat->entries; i++) + BE32_IN(&bat->bat[i]); +} + +void +vhd_bat_out(vhd_bat_t *bat) +{ + int i; + + for (i = 0; i < bat->entries; i++) + BE32_OUT(&bat->bat[i]); +} + +uint32_t +vhd_checksum_footer(vhd_footer_t *footer) +{ + int i; + unsigned char *blob; + uint32_t checksum, tmp; + + checksum = 0; + tmp = footer->checksum; + footer->checksum = 0; + + blob = (unsigned char *)footer; + for (i = 0; i < sizeof(vhd_footer_t); i++) + checksum += (uint32_t)blob[i]; + + footer->checksum = tmp; + return ~checksum; +} + +int +vhd_validate_footer(vhd_footer_t *footer) +{ + int csize; + uint32_t checksum; + + csize = sizeof(footer->cookie); + if (memcmp(footer->cookie, HD_COOKIE, csize) != 0 && + memcmp(footer->cookie, VHD_POISON_COOKIE, csize) != 0) { + char buf[9]; + strncpy(buf, footer->cookie, sizeof(buf)); + buf[sizeof(buf)-1]= '\0'; + VHDLOG("invalid footer cookie: %s\n", buf); + return -EINVAL; + } + + checksum = vhd_checksum_footer(footer); + if (checksum != footer->checksum) { + /* + * early td-util did not re-calculate + * checksum when marking vhds 'hidden' + */ + if (footer->hidden && + !strncmp(footer->crtr_app, "tap", 3) && + (footer->crtr_ver == VHD_VERSION(0, 1) || + footer->crtr_ver == VHD_VERSION(1, 1))) { + char tmp = footer->hidden; + footer->hidden = 0; + checksum = vhd_checksum_footer(footer); + footer->hidden = tmp; + + if (checksum == footer->checksum) + return 0; + } + + VHDLOG("invalid footer checksum: " + "footer = 0x%08x, calculated = 0x%08x\n", + footer->checksum, checksum); + return -EINVAL; + } + + return 0; +} + +uint32_t +vhd_checksum_header(vhd_header_t *header) +{ + int i; + unsigned char *blob; + uint32_t checksum, tmp; + + checksum = 0; + tmp = header->checksum; + header->checksum = 0; + + blob = (unsigned char *)header; + for (i = 0; i < sizeof(vhd_header_t); i++) + checksum += (uint32_t)blob[i]; + + header->checksum = tmp; + return ~checksum; +} + +int +vhd_validate_header(vhd_header_t *header) +{ + int i, n; + uint32_t checksum; + + if (memcmp(header->cookie, DD_COOKIE, 8) != 0) { + char buf[9]; + strncpy(buf, header->cookie, sizeof(buf)); + buf[sizeof(buf)-1]= '\0'; + VHDLOG("invalid header cookie: %s\n", buf); + return -EINVAL; + } + + if (header->hdr_ver != 0x00010000) { + VHDLOG("invalid header version 0x%08x\n", header->hdr_ver); + return -EINVAL; + } + + if (header->data_offset != 0xFFFFFFFFFFFFFFFF) { + VHDLOG("invalid header data_offset 0x%016"PRIx64"\n", + header->data_offset); + return -EINVAL; + } + + n = sizeof(header->loc) / sizeof(vhd_parent_locator_t); + for (i = 0; i < n; i++) + if (vhd_validate_platform_code(header->loc[i].code)) + return -EINVAL; + + checksum = vhd_checksum_header(header); + if (checksum != header->checksum) { + VHDLOG("invalid header checksum: " + "header = 0x%08x, calculated = 0x%08x\n", + header->checksum, checksum); + return -EINVAL; + } + + return 0; +} + +static inline int +vhd_validate_bat(vhd_bat_t *bat) +{ + if (!bat->bat) + return -EINVAL; + + return 0; +} + +uint32_t +vhd_checksum_batmap(vhd_batmap_t *batmap) +{ + int i, n; + char *blob; + uint32_t checksum; + + blob = batmap->map; + checksum = 0; + + n = vhd_sectors_to_bytes(batmap->header.batmap_size); + + for (i = 0; i < n; i++) { + if (batmap->header.batmap_version == VHD_BATMAP_VERSION(1, 1)) + checksum += (uint32_t)blob[i]; + else + checksum += (uint32_t)(unsigned char)blob[i]; + } + + return ~checksum; +} + +int +vhd_validate_batmap_header(vhd_batmap_t *batmap) +{ + if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, 8)) + return -EINVAL; + + if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION) + return -EINVAL; + + return 0; +} + +int +vhd_validate_batmap(vhd_batmap_t *batmap) +{ + uint32_t checksum; + + if (!batmap->map) + return -EINVAL; + + checksum = vhd_checksum_batmap(batmap); + if (checksum != batmap->header.checksum) + return -EINVAL; + + return 0; +} + +int +vhd_batmap_header_offset(vhd_context_t *ctx, off_t *_off) +{ + off_t off; + size_t bat; + + *_off = 0; + + off = ctx->header.table_offset; + bat = ctx->header.max_bat_size * sizeof(uint32_t); + off += vhd_bytes_padded(bat); + + *_off = off; + return 0; +} + +int +vhd_validate_platform_code(uint32_t code) +{ + switch (code) { + case PLAT_CODE_NONE: + case PLAT_CODE_WI2R: + case PLAT_CODE_WI2K: + case PLAT_CODE_W2RU: + case PLAT_CODE_W2KU: + case PLAT_CODE_MAC: + case PLAT_CODE_MACX: + return 0; + default: + VHDLOG("invalid parent locator code %u\n", code); + return -EINVAL; + } +} + +int +vhd_parent_locator_count(vhd_context_t *ctx) +{ + return (sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t)); +} + +int +vhd_hidden(vhd_context_t *ctx, int *hidden) +{ + int err; + + *hidden = 0; + + if (vhd_type_dynamic(ctx) && vhd_creator_tapdisk(ctx) && + (ctx->footer.crtr_ver == VHD_VERSION(0, 1) || + ctx->footer.crtr_ver == VHD_VERSION(1, 1))) { + vhd_footer_t copy; + + err = vhd_read_footer_at(ctx, ©, 0); + if (err) { + VHDLOG("error reading backup footer of %s: %d\n", + ctx->file, err); + return err; + } + *hidden = copy.hidden; + } else + *hidden = ctx->footer.hidden; + + return 0; +} + +int +vhd_chain_depth(vhd_context_t *ctx, int *depth) +{ + char *file; + int err, cnt; + vhd_context_t vhd, *cur; + + err = 0; + cnt = 0; + *depth = 0; + file = NULL; + cur = ctx; + + for (;;) { + cnt++; + + if (cur->footer.type != HD_TYPE_DIFF) + break; + + if (vhd_parent_raw(cur)) { + cnt++; + break; + } + + err = vhd_parent_locator_get(cur, &file); + if (err) { + file = NULL; + break; + } + + if (cur != ctx) { + vhd_close(cur); + cur = NULL; + } + + err = vhd_open(&vhd, file, VHD_OPEN_RDONLY); + if (err) + break; + + cur = &vhd; + free(file); + file = NULL; + } + + free(file); + if (cur && cur != ctx) + vhd_close(cur); + + if (!err) + *depth = cnt; + + return err; +} + +int +vhd_batmap_test(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block) +{ + if (!vhd_has_batmap(ctx) || !batmap->map) + return 0; + + if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3))) + return 0; + + return test_bit(batmap->map, block); +} + +void +vhd_batmap_set(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block) +{ + if (!vhd_has_batmap(ctx) || !batmap->map) + return; + + if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3))) + return; + + set_bit(batmap->map, block); +} + +void +vhd_batmap_clear(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block) +{ + if (!vhd_has_batmap(ctx) || !batmap->map) + return; + + if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3))) + return; + + clear_bit(batmap->map, block); +} + +int +vhd_bitmap_test(vhd_context_t *ctx, char *map, uint32_t block) +{ + if (vhd_creator_tapdisk(ctx) && + ctx->footer.crtr_ver == 0x00000001) + return old_test_bit(map, block); + + return test_bit(map, block); +} + +void +vhd_bitmap_set(vhd_context_t *ctx, char *map, uint32_t block) +{ + if (vhd_creator_tapdisk(ctx) && + ctx->footer.crtr_ver == 0x00000001) + return old_set_bit(map, block); + + return set_bit(map, block); +} + +void +vhd_bitmap_clear(vhd_context_t *ctx, char *map, uint32_t block) +{ + if (vhd_creator_tapdisk(ctx) && + ctx->footer.crtr_ver == 0x00000001) + return old_clear_bit(map, block); + + return clear_bit(map, block); +} + +/* + * returns absolute offset of the first + * byte of the file which is not vhd metadata + */ +int +vhd_end_of_headers(vhd_context_t *ctx, off_t *end) +{ + int err, i, n; + uint32_t bat_bytes; + off_t eom, bat_end; + vhd_parent_locator_t *loc; + + *end = 0; + + if (!vhd_type_dynamic(ctx)) + return 0; + + eom = ctx->footer.data_offset + sizeof(vhd_header_t); + + bat_bytes = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t)); + bat_end = ctx->header.table_offset + bat_bytes; + + eom = MAX(eom, bat_end); + + if (vhd_has_batmap(ctx)) { + off_t hdr_end, hdr_secs, map_end, map_secs; + + err = vhd_get_batmap(ctx); + if (err) + return err; + + hdr_secs = secs_round_up_no_zero(sizeof(vhd_batmap_header_t)); + err = vhd_batmap_header_offset(ctx, &hdr_end); + if (err) + return err; + + hdr_end += vhd_sectors_to_bytes(hdr_secs); + eom = MAX(eom, hdr_end); + + map_secs = ctx->batmap.header.batmap_size; + map_end = (ctx->batmap.header.batmap_offset + + vhd_sectors_to_bytes(map_secs)); + eom = MAX(eom, map_end); + } + + /* parent locators */ + n = sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t); + + for (i = 0; i < n; i++) { + off_t loc_end; + + loc = &ctx->header.loc[i]; + if (loc->code == PLAT_CODE_NONE) + continue; + + loc_end = loc->data_offset + vhd_parent_locator_size(loc); + eom = MAX(eom, loc_end); + } + + *end = eom; + return 0; +} + +int +vhd_end_of_data(vhd_context_t *ctx, off_t *end) +{ + int i, err; + off_t max; + uint64_t blk; + + if (!vhd_type_dynamic(ctx)) { + err = vhd_seek(ctx, 0, SEEK_END); + if (err) + return err; + + max = vhd_position(ctx); + if (max == (off_t)-1) + return -errno; + + *end = max - sizeof(vhd_footer_t); + return 0; + } + + err = vhd_end_of_headers(ctx, &max); + if (err) + return err; + + err = vhd_get_bat(ctx); + if (err) + return err; + + max >>= VHD_SECTOR_SHIFT; + + for (i = 0; i < ctx->bat.entries; i++) { + blk = ctx->bat.bat[i]; + + if (blk != DD_BLK_UNUSED) { + blk += ctx->spb + ctx->bm_secs; + max = MAX(blk, max); + } + } + + *end = vhd_sectors_to_bytes(max); + return 0; +} + +uint32_t +vhd_time(time_t time) +{ + struct tm tm; + time_t micro_epoch; + + memset(&tm, 0, sizeof(struct tm)); + tm.tm_year = 100; + tm.tm_mon = 0; + tm.tm_mday = 1; + micro_epoch = mktime(&tm); + + return (uint32_t)(time - micro_epoch); +} + +/* + * Stringify the VHD timestamp for printing. + * As with ctime_r, target must be >=26 bytes. + */ +size_t +vhd_time_to_string(uint32_t timestamp, char *target) +{ + char *cr; + struct tm tm; + time_t t1, t2; + + memset(&tm, 0, sizeof(struct tm)); + + /* VHD uses an epoch of 12:00AM, Jan 1, 2000. */ + /* Need to adjust this to the expected epoch of 1970. */ + tm.tm_year = 100; + tm.tm_mon = 0; + tm.tm_mday = 1; + + t1 = mktime(&tm); + t2 = t1 + (time_t)timestamp; + ctime_r(&t2, target); + + /* handle mad ctime_r newline appending. */ + if ((cr = strchr(target, '\n')) != NULL) + *cr = '\0'; + + return (strlen(target)); +} + +/* + * nabbed from vhd specs. + */ +uint32_t +vhd_chs(uint64_t size) +{ + uint32_t secs, cylinders, heads, spt, cth; + + secs = secs_round_up_no_zero(size); + + if (secs > 65535 * 16 * 255) + secs = 65535 * 16 * 255; + + if (secs >= 65535 * 16 * 63) { + spt = 255; + cth = secs / spt; + heads = 16; + } else { + spt = 17; + cth = secs / spt; + heads = (cth + 1023) / 1024; + + if (heads < 4) + heads = 4; + + if (cth >= (heads * 1024) || heads > 16) { + spt = 31; + cth = secs / spt; + heads = 16; + } + + if (cth >= heads * 1024) { + spt = 63; + cth = secs / spt; + heads = 16; + } + } + + cylinders = cth / heads; + + return GEOM_ENCODE(cylinders, heads, spt); +} + +int +vhd_get_footer(vhd_context_t *ctx) +{ + if (!vhd_validate_footer(&ctx->footer)) + return 0; + + return vhd_read_footer(ctx, &ctx->footer); +} + +int +vhd_get_header(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + if (!vhd_validate_header(&ctx->header)) + return 0; + + return vhd_read_header(ctx, &ctx->header); +} + +int +vhd_get_bat(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + if (!vhd_validate_bat(&ctx->bat)) + return 0; + + vhd_put_bat(ctx); + return vhd_read_bat(ctx, &ctx->bat); +} + +int +vhd_get_batmap(vhd_context_t *ctx) +{ + if (!vhd_has_batmap(ctx)) + return -EINVAL; + + if (!vhd_validate_batmap(&ctx->batmap)) + return 0; + + vhd_put_batmap(ctx); + return vhd_read_batmap(ctx, &ctx->batmap); +} + +void +vhd_put_footer(vhd_context_t *ctx) +{ + memset(&ctx->footer, 0, sizeof(vhd_footer_t)); +} + +void +vhd_put_header(vhd_context_t *ctx) +{ + memset(&ctx->header, 0, sizeof(vhd_header_t)); +} + +void +vhd_put_bat(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return; + + free(ctx->bat.bat); + memset(&ctx->bat, 0, sizeof(vhd_bat_t)); +} + +void +vhd_put_batmap(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return; + + if (!vhd_has_batmap(ctx)) + return; + + free(ctx->batmap.map); + memset(&ctx->batmap, 0, sizeof(vhd_batmap_t)); +} + +/* + * look for 511 byte footer at end of file + */ +int +vhd_read_short_footer(vhd_context_t *ctx, vhd_footer_t *footer) +{ + int err; + char *buf; + off_t eof; + + buf = NULL; + + err = vhd_seek(ctx, 0, SEEK_END); + if (err) + goto out; + + eof = vhd_position(ctx); + if (eof == (off_t)-1) { + err = -errno; + goto out; + } + + err = vhd_seek(ctx, eof - 511, SEEK_SET); + if (err) + goto out; + + err = posix_memalign((void **)&buf, + VHD_SECTOR_SIZE, sizeof(vhd_footer_t)); + if (err) { + buf = NULL; + err = -err; + goto out; + } + + memset(buf, 0, sizeof(vhd_footer_t)); + + /* + * expecting short read here + */ + vhd_read(ctx, buf, sizeof(vhd_footer_t)); + + memcpy(footer, buf, sizeof(vhd_footer_t)); + + vhd_footer_in(footer); + err = vhd_validate_footer(footer); + +out: + if (err) + VHDLOG("%s: failed reading short footer: %d\n", + ctx->file, err); + free(buf); + return err; +} + +int +vhd_read_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off) +{ + int err; + char *buf; + + buf = NULL; + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + err = posix_memalign((void **)&buf, + VHD_SECTOR_SIZE, sizeof(vhd_footer_t)); + if (err) { + buf = NULL; + err = -err; + goto out; + } + + err = vhd_read(ctx, buf, sizeof(vhd_footer_t)); + if (err) + goto out; + + memcpy(footer, buf, sizeof(vhd_footer_t)); + + vhd_footer_in(footer); + err = vhd_validate_footer(footer); + +out: + if (err) + VHDLOG("%s: reading footer at 0x%08"PRIx64" failed: %d\n", + ctx->file, off, err); + free(buf); + return err; +} + +int +vhd_read_footer(vhd_context_t *ctx, vhd_footer_t *footer) +{ + int err; + off_t off; + + err = vhd_seek(ctx, 0, SEEK_END); + if (err) + return err; + + off = vhd_position(ctx); + if (off == (off_t)-1) + return -errno; + + err = vhd_read_footer_at(ctx, footer, off - 512); + if (err != -EINVAL) + return err; + + err = vhd_read_short_footer(ctx, footer); + if (err != -EINVAL) + return err; + + if (ctx->oflags & VHD_OPEN_STRICT) + return -EINVAL; + + return vhd_read_footer_at(ctx, footer, 0); +} + +int +vhd_read_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off) +{ + int err; + char *buf; + + buf = NULL; + + if (!vhd_type_dynamic(ctx)) { + err = -EINVAL; + goto out; + } + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + err = posix_memalign((void **)&buf, + VHD_SECTOR_SIZE, sizeof(vhd_header_t)); + if (err) { + buf = NULL; + err = -err; + goto out; + } + + err = vhd_read(ctx, buf, sizeof(vhd_header_t)); + if (err) + goto out; + + memcpy(header, buf, sizeof(vhd_header_t)); + + vhd_header_in(header); + err = vhd_validate_header(header); + +out: + if (err) + VHDLOG("%s: reading header at 0x%08"PRIx64" failed: %d\n", + ctx->file, off, err); + free(buf); + return err; +} + +int +vhd_read_header(vhd_context_t *ctx, vhd_header_t *header) +{ + int err; + off_t off; + + if (!vhd_type_dynamic(ctx)) { + VHDLOG("%s is not dynamic!\n", ctx->file); + return -EINVAL; + } + + off = ctx->footer.data_offset; + return vhd_read_header_at(ctx, header, off); +} + +int +vhd_read_bat(vhd_context_t *ctx, vhd_bat_t *bat) +{ + int err; + char *buf; + off_t off; + size_t size; + + buf = NULL; + + if (!vhd_type_dynamic(ctx)) { + err = -EINVAL; + goto fail; + } + + off = ctx->header.table_offset; + size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t)); + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + buf = NULL; + err = -err; + goto fail; + } + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto fail; + + err = vhd_read(ctx, buf, size); + if (err) + goto fail; + + bat->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT; + bat->entries = ctx->header.max_bat_size; + bat->bat = (uint32_t *)buf; + + vhd_bat_in(bat); + + return 0; + +fail: + free(buf); + memset(bat, 0, sizeof(vhd_bat_t)); + VHDLOG("%s: failed to read bat: %d\n", ctx->file, err); + return err; +} + +static int +vhd_read_batmap_header(vhd_context_t *ctx, vhd_batmap_t *batmap) +{ + int err; + char *buf; + off_t off; + size_t size; + + buf = NULL; + + err = vhd_batmap_header_offset(ctx, &off); + if (err) + goto fail; + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto fail; + + size = vhd_bytes_padded(sizeof(vhd_batmap_header_t)); + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + buf = NULL; + err = -err; + goto fail; + } + + err = vhd_read(ctx, buf, size); + if (err) + goto fail; + + memcpy(&batmap->header, buf, sizeof(vhd_batmap_header_t)); + free(buf); + buf = NULL; + + vhd_batmap_header_in(batmap); + + return 0; + +fail: + free(buf); + memset(&batmap->header, 0, sizeof(vhd_batmap_header_t)); + VHDLOG("%s: failed to read batmap header: %d\n", ctx->file, err); + return err; +} + +static int +vhd_read_batmap_map(vhd_context_t *ctx, vhd_batmap_t *batmap) +{ + int err; + char *buf; + off_t off; + size_t map_size; + + map_size = vhd_sectors_to_bytes(batmap->header.batmap_size); + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, map_size); + if (err) { + buf = NULL; + err = -err; + goto fail; + } + + off = batmap->header.batmap_offset; + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto fail; + + err = vhd_read(ctx, buf, map_size); + if (err) + goto fail; + + batmap->map = buf; + return 0; + +fail: + free(buf); + batmap->map = NULL; + VHDLOG("%s: failed to read batmap: %d\n", ctx->file, err); + return err; +} + +int +vhd_read_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap) +{ + int err; + + if (!vhd_has_batmap(ctx)) + return -EINVAL; + + memset(batmap, 0, sizeof(vhd_batmap_t)); + + err = vhd_read_batmap_header(ctx, batmap); + if (err) + return err; + + err = vhd_validate_batmap_header(batmap); + if (err) + return err; + + err = vhd_read_batmap_map(ctx, batmap); + if (err) + return err; + + err = vhd_validate_batmap(batmap); + if (err) + goto fail; + + return 0; + +fail: + free(batmap->map); + memset(batmap, 0, sizeof(vhd_batmap_t)); + return err; +} + +int +vhd_has_batmap(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return 0; + + if (!vhd_creator_tapdisk(ctx)) + return 0; + + if (ctx->footer.crtr_ver <= VHD_VERSION(0, 1)) + return 0; + + if (ctx->footer.crtr_ver >= VHD_VERSION(1, 2)) + return 1; + + /* + * VHDs of version 1.1 probably have a batmap, but may not + * if they were updated from version 0.1 via vhd-update. + */ + if (!vhd_validate_batmap_header(&ctx->batmap)) + return 1; + + if (vhd_read_batmap_header(ctx, &ctx->batmap)) + return 0; + + return (!vhd_validate_batmap_header(&ctx->batmap)); +} + +/* + * Is this a block device (with a fixed size)? This affects whether the file + * can be truncated and where the footer is written for VHDs. + */ +int +vhd_test_file_fixed(const char *file, int *is_block) +{ + int err; + struct stat stats; + + err = stat(file, &stats); + if (err == -1) + return -errno; + + *is_block = !!(S_ISBLK(stats.st_mode)); + return err; +} + +int +vhd_find_parent(vhd_context_t *ctx, const char *parent, char **_location) +{ + int err; + char *location, *cpath, *cdir, *path; + + err = 0; + path = NULL; + cpath = NULL; + location = NULL; + *_location = NULL; + + if (!parent) + return -EINVAL; + + if (parent[0] == '/') { + if (!access(parent, R_OK)) { + path = strdup(parent); + if (!path) + return -ENOMEM; + *_location = path; + return 0; + } + } + + /* check parent path relative to child's directory */ + cpath = realpath(ctx->file, NULL); + if (!cpath) { + err = -errno; + goto out; + } + + cdir = dirname(cpath); + if (asprintf(&location, "%s/%s", cdir, parent) == -1) { + err = -errno; + location = NULL; + goto out; + } + + if (!access(location, R_OK)) { + path = realpath(location, NULL); + if (path) { + *_location = path; + return 0; + } + } + err = -errno; + +out: + free(location); + free(cpath); + return err; +} + +static int +vhd_macx_encode_location(char *name, char **out, int *outlen) +{ + iconv_t cd; + int len, err; + size_t ibl, obl; + char *uri, *uri_utf8, *uri_utf8p, *ret; + const char *urip; + + err = 0; + ret = NULL; + *out = NULL; + *outlen = 0; + len = strlen(name) + strlen("file://"); + + ibl = len; + obl = len; + + urip = uri = malloc(ibl + 1); + uri_utf8 = uri_utf8p = malloc(obl); + + if (!uri || !uri_utf8) + return -ENOMEM; + + cd = iconv_open("UTF-8", "ASCII"); + if (cd == (iconv_t)-1) { + err = -errno; + goto out; + } + + snprintf(uri, ibl+1, "file://%s", name); + + if (iconv(cd, +#ifdef __linux__ + (char **) +#endif + &urip, &ibl, &uri_utf8p, &obl) == (size_t)-1 || + ibl || obl) { + err = (errno ? -errno : -EIO); + goto out; + } + + ret = malloc(len); + if (!ret) { + err = -ENOMEM; + goto out; + } + + memcpy(ret, uri_utf8, len); + *outlen = len; + *out = ret; + + out: + free(uri); + free(uri_utf8); + if (cd != (iconv_t)-1) + iconv_close(cd); + + return err; +} + +static int +vhd_w2u_encode_location(char *name, char **out, int *outlen) +{ + iconv_t cd; + int len, err; + size_t ibl, obl; + char *uri, *uri_utf16, *uri_utf16p, *tmp, *ret; + const char *urip; + + err = 0; + ret = NULL; + *out = NULL; + *outlen = 0; + cd = (iconv_t) -1; + + /* + * MICROSOFT_COMPAT + * relative paths must start with ".\" + */ + if (name[0] != '/') { + tmp = strstr(name, "./"); + if (tmp == name) + tmp += strlen("./"); + else + tmp = name; + + err = asprintf(&uri, ".\\%s", tmp); + } else + err = asprintf(&uri, "%s", name); + + if (err == -1) + return -ENOMEM; + + tmp = uri; + while (*tmp != '\0') { + if (*tmp == '/') + *tmp = '\\'; + tmp++; + } + + len = strlen(uri); + ibl = len; + obl = len * 2; + urip = uri; + + uri_utf16 = uri_utf16p = malloc(obl); + if (!uri_utf16) { + err = -ENOMEM; + goto out; + } + + /* + * MICROSOFT_COMPAT + * little endian unicode here + */ + cd = iconv_open("UTF-16LE", "ASCII"); + if (cd == (iconv_t)-1) { + err = -errno; + goto out; + } + + if (iconv(cd, +#ifdef __linux__ + (char **) +#endif + &urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 || + ibl || obl) { + err = (errno ? -errno : -EIO); + goto out; + } + + len = len * 2; + ret = malloc(len); + if (!ret) { + err = -ENOMEM; + goto out; + } + + memcpy(ret, uri_utf16, len); + *outlen = len; + *out = ret; + err = 0; + + out: + free(uri); + free(uri_utf16); + if (cd != (iconv_t)-1) + iconv_close(cd); + + return err; +} + +static char * +vhd_macx_decode_location(const char *in, char *out, int len) +{ + iconv_t cd; + char *name; + size_t ibl, obl; + + name = out; + ibl = obl = len; + + cd = iconv_open("ASCII", "UTF-8"); + if (cd == (iconv_t)-1) + return NULL; + + if (iconv(cd, +#ifdef __linux__ + (char **) +#endif + &in, &ibl, &out, &obl) == (size_t)-1 || ibl) + return NULL; + + iconv_close(cd); + *out = '\0'; + + if (strstr(name, "file://") != name) + return NULL; + + name += strlen("file://"); + + return strdup(name); +} + +static char * +vhd_w2u_decode_location(const char *in, char *out, int len, char *utf_type) +{ + iconv_t cd; + char *name, *tmp; + size_t ibl, obl; + + tmp = name = out; + ibl = obl = len; + + cd = iconv_open("ASCII", utf_type); + if (cd == (iconv_t)-1) + return NULL; + + if (iconv(cd, +#ifdef __linux__ + (char **) +#endif + &in, &ibl, &out, &obl) == (size_t)-1 || ibl) + return NULL; + + iconv_close(cd); + *out = '\0'; + + /* TODO: spaces */ + while (tmp != out) { + if (*tmp == '\\') + *tmp = '/'; + tmp++; + } + + if (strstr(name, "C:") == name || strstr(name, "c:") == name) + name += strlen("c:"); + + return strdup(name); +} + +int +vhd_header_decode_parent(vhd_context_t *ctx, vhd_header_t *header, char **buf) +{ + char *code, out[512]; + + if (vhd_creator_tapdisk(ctx) && + ctx->footer.crtr_ver == VHD_VERSION(0, 1)) + code = UTF_16; + else + code = UTF_16BE; + + *buf = vhd_w2u_decode_location(header->prt_name, out, 512, code); + return (*buf == NULL ? -EINVAL : 0); +} + +int +vhd_parent_locator_read(vhd_context_t *ctx, + vhd_parent_locator_t *loc, char **parent) +{ + int err, size; + char *raw, *out, *name; + + raw = NULL; + out = NULL; + name = NULL; + *parent = NULL; + + if (ctx->footer.type != HD_TYPE_DIFF) { + err = -EINVAL; + goto out; + } + + switch (loc->code) { + case PLAT_CODE_MACX: + case PLAT_CODE_W2KU: + case PLAT_CODE_W2RU: + break; + default: + err = -EINVAL; + goto out; + } + + err = vhd_seek(ctx, loc->data_offset, SEEK_SET); + if (err) + goto out; + + size = vhd_parent_locator_size(loc); + if (size <= 0) { + err = -EINVAL; + goto out; + } + + err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size); + if (err) { + raw = NULL; + err = -err; + goto out; + } + + err = vhd_read(ctx, raw, size); + if (err) + goto out; + + out = malloc(loc->data_len + 1); + if (!out) { + err = -ENOMEM; + goto out; + } + + switch (loc->code) { + case PLAT_CODE_MACX: + name = vhd_macx_decode_location(raw, out, loc->data_len); + break; + case PLAT_CODE_W2KU: + case PLAT_CODE_W2RU: + name = vhd_w2u_decode_location(raw, out, + loc->data_len, UTF_16LE); + break; + } + + if (!name) { + err = -EINVAL; + goto out; + } + + err = 0; + *parent = name; + +out: + free(raw); + free(out); + + if (err) { + VHDLOG("%s: error reading parent locator: %d\n", + ctx->file, err); + VHDLOG("%s: locator: code %u, space 0x%x, len 0x%x, " + "off 0x%"PRIx64"\n", ctx->file, loc->code, loc->data_space, + loc->data_len, loc->data_offset); + } + + return err; +} + +int +vhd_parent_locator_get(vhd_context_t *ctx, char **parent) +{ + int i, n, err; + char *name, *location; + vhd_parent_locator_t *loc; + + err = 0; + *parent = NULL; + + if (ctx->footer.type != HD_TYPE_DIFF) + return -EINVAL; + + n = vhd_parent_locator_count(ctx); + for (i = 0; i < n; i++) { + loc = ctx->header.loc + i; + err = vhd_parent_locator_read(ctx, loc, &name); + if (err) + continue; + + err = vhd_find_parent(ctx, name, &location); + if (err) + VHDLOG("%s: couldn't find parent %s (%d)\n", + ctx->file, name, err); + free(name); + + if (!err) { + *parent = location; + return 0; + } + } + + return err; +} + +int +vhd_parent_locator_write_at(vhd_context_t *ctx, + const char *parent, off_t off, uint32_t code, + size_t max_bytes, vhd_parent_locator_t *loc) +{ + struct stat stats; + int err, len, size; + char *absolute_path, *relative_path, *encoded, *block; + + memset(loc, 0, sizeof(vhd_parent_locator_t)); + + if (ctx->footer.type != HD_TYPE_DIFF) + return -EINVAL; + + absolute_path = NULL; + relative_path = NULL; + encoded = NULL; + block = NULL; + size = 0; + len = 0; + + switch (code) { + case PLAT_CODE_MACX: + case PLAT_CODE_W2KU: + case PLAT_CODE_W2RU: + break; + default: + return -EINVAL; + } + + absolute_path = realpath(parent, NULL); + if (!absolute_path) { + err = -errno; + goto out; + } + + err = stat(absolute_path, &stats); + if (err) { + err = -errno; + goto out; + } + + if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) { + err = -EINVAL; + goto out; + } + + relative_path = relative_path_to(ctx->file, absolute_path, &err); + if (!relative_path || err) { + err = (err ? err : -EINVAL); + goto out; + } + + switch (code) { + case PLAT_CODE_MACX: + err = vhd_macx_encode_location(relative_path, &encoded, &len); + break; + case PLAT_CODE_W2KU: + case PLAT_CODE_W2RU: + err = vhd_w2u_encode_location(relative_path, &encoded, &len); + break; + default: + err = -EINVAL; + } + + if (err) + goto out; + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + size = vhd_bytes_padded(len); + + if (max_bytes && size > max_bytes) { + err = -ENAMETOOLONG; + goto out; + } + + err = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size); + if (err) { + block = NULL; + err = -err; + goto out; + } + + memset(block, 0, size); + memcpy(block, encoded, len); + + err = vhd_write(ctx, block, size); + if (err) + goto out; + + err = 0; + +out: + free(absolute_path); + free(relative_path); + free(encoded); + free(block); + + if (!err) { + loc->res = 0; + loc->code = code; + loc->data_len = len; + /* + * write number of bytes ('size') instead of number of sectors + * into loc->data_space to be compatible with MSFT, even though + * this goes against the specs + */ + loc->data_space = size; + loc->data_offset = off; + } + + return err; +} + +static int +vhd_footer_offset_at_eof(vhd_context_t *ctx, off_t *off) +{ + int err; + if ((err = vhd_seek(ctx, 0, SEEK_END))) + return errno; + *off = vhd_position(ctx) - sizeof(vhd_footer_t); + return 0; +} + +int +vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp) +{ + int err; + char *buf; + size_t size; + off_t off; + uint64_t blk; + + buf = NULL; + *bufp = NULL; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_get_bat(ctx); + if (err) + return err; + + if (block >= ctx->bat.entries) + return -ERANGE; + + blk = ctx->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return -EINVAL; + + off = vhd_sectors_to_bytes(blk); + size = vhd_bytes_padded(ctx->spb >> 3); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) + return -err; + + err = vhd_read(ctx, buf, size); + if (err) + goto fail; + + *bufp = buf; + return 0; + +fail: + free(buf); + return err; +} + +int +vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp) +{ + int err; + char *buf; + size_t size; + uint64_t blk; + off_t end, off; + + buf = NULL; + *bufp = NULL; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_get_bat(ctx); + if (err) + return err; + + if (block >= ctx->bat.entries) + return -ERANGE; + + blk = ctx->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return -EINVAL; + + off = vhd_sectors_to_bytes(blk + ctx->bm_secs); + size = vhd_sectors_to_bytes(ctx->spb); + + err = vhd_footer_offset_at_eof(ctx, &end); + if (err) + return err; + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + err = -err; + goto fail; + } + + if (end < off + ctx->header.block_size) { + size = end - off; + memset(buf + size, 0, ctx->header.block_size - size); + } + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto fail; + + err = vhd_read(ctx, buf, size); + if (err) + goto fail; + + *bufp = buf; + return 0; + +fail: + free(buf); + return err; +} + +int +vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off) +{ + int err; + vhd_footer_t *f; + + f = NULL; + + err = posix_memalign((void **)&f, + VHD_SECTOR_SIZE, sizeof(vhd_footer_t)); + if (err) { + f = NULL; + err = -err; + goto out; + } + + memcpy(f, footer, sizeof(vhd_footer_t)); + f->checksum = vhd_checksum_footer(f); + + err = vhd_validate_footer(f); + if (err) + goto out; + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + vhd_footer_out(f); + + err = vhd_write(ctx, f, sizeof(vhd_footer_t)); + +out: + if (err) + VHDLOG("%s: failed writing footer at 0x%08"PRIx64": %d\n", + ctx->file, off, err); + free(f); + return err; +} + +int +vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer) +{ + int err; + off_t off; + + if (ctx->is_block) + err = vhd_footer_offset_at_eof(ctx, &off); + else + err = vhd_end_of_data(ctx, &off); + if (err) + return err; + + err = vhd_write_footer_at(ctx, footer, off); + if (err) + return err; + + if (!vhd_type_dynamic(ctx)) + return 0; + + return vhd_write_footer_at(ctx, footer, 0); +} + +int +vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off) +{ + int err; + vhd_header_t *h; + + h = NULL; + + if (!vhd_type_dynamic(ctx)) { + err = -EINVAL; + goto out; + } + + err = posix_memalign((void **)&h, + VHD_SECTOR_SIZE, sizeof(vhd_header_t)); + if (err) { + h = NULL; + err = -err; + goto out; + } + + memcpy(h, header, sizeof(vhd_header_t)); + + h->checksum = vhd_checksum_header(h); + err = vhd_validate_header(h); + if (err) + goto out; + + vhd_header_out(h); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + err = vhd_write(ctx, h, sizeof(vhd_header_t)); + +out: + if (err) + VHDLOG("%s: failed writing header at 0x%08"PRIx64": %d\n", + ctx->file, off, err); + free(h); + return err; +} + +int +vhd_write_header(vhd_context_t *ctx, vhd_header_t *header) +{ + int err; + off_t off; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + off = ctx->footer.data_offset; + return vhd_write_header_at(ctx, header, off); +} + +int +vhd_write_bat(vhd_context_t *ctx, vhd_bat_t *bat) +{ + int err; + off_t off; + vhd_bat_t b; + size_t size; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_validate_bat(&ctx->bat); + if (err) + return err; + + err = vhd_validate_bat(bat); + if (err) + return err; + + memset(&b, 0, sizeof(vhd_bat_t)); + + off = ctx->header.table_offset; + size = vhd_bytes_padded(bat->entries * sizeof(uint32_t)); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + + err = posix_memalign((void **)&b.bat, VHD_SECTOR_SIZE, size); + if (err) + return -err; + + memcpy(b.bat, bat->bat, size); + b.spb = bat->spb; + b.entries = bat->entries; + vhd_bat_out(&b); + + err = vhd_write(ctx, b.bat, size); + free(b.bat); + + return err; +} + +int +vhd_write_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap) +{ + int err; + off_t off; + vhd_batmap_t b; + char *buf, *map; + size_t size, map_size; + + buf = NULL; + map = NULL; + + if (!vhd_has_batmap(ctx)) { + err = -EINVAL; + goto out; + } + + b.header = batmap->header; + b.map = batmap->map; + + b.header.checksum = vhd_checksum_batmap(&b); + err = vhd_validate_batmap(&b); + if (err) + goto out; + + off = b.header.batmap_offset; + map_size = vhd_sectors_to_bytes(b.header.batmap_size); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + err = posix_memalign((void **)&map, VHD_SECTOR_SIZE, map_size); + if (err) { + map = NULL; + err = -err; + goto out; + } + + memcpy(map, b.map, map_size); + + err = vhd_write(ctx, map, map_size); + if (err) + goto out; + + err = vhd_batmap_header_offset(ctx, &off); + if (err) + goto out; + + size = vhd_bytes_padded(sizeof(vhd_batmap_header_t)); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + err = -err; + buf = NULL; + goto out; + } + + vhd_batmap_header_out(&b); + memset(buf, 0, size); + memcpy(buf, &b.header, sizeof(vhd_batmap_header_t)); + + err = vhd_write(ctx, buf, size); + +out: + if (err) + VHDLOG("%s: failed writing batmap: %d\n", ctx->file, err); + free(buf); + free(map); + return 0; +} + +int +vhd_write_bitmap(vhd_context_t *ctx, uint32_t block, char *bitmap) +{ + int err; + off_t off; + uint64_t blk; + size_t secs, size; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_validate_bat(&ctx->bat); + if (err) + return err; + + if (block >= ctx->bat.entries) + return -ERANGE; + + if ((unsigned long)bitmap & (VHD_SECTOR_SIZE - 1)) + return -EINVAL; + + blk = ctx->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return -EINVAL; + + off = vhd_sectors_to_bytes(blk); + size = vhd_sectors_to_bytes(ctx->bm_secs); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + + err = vhd_write(ctx, bitmap, size); + if (err) + return err; + + return 0; +} + +int +vhd_write_block(vhd_context_t *ctx, uint32_t block, char *data) +{ + int err; + off_t off; + size_t size; + uint64_t blk; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_validate_bat(&ctx->bat); + if (err) + return err; + + if (block >= ctx->bat.entries) + return -ERANGE; + + if ((unsigned long)data & ~(VHD_SECTOR_SIZE -1)) + return -EINVAL; + + blk = ctx->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return -EINVAL; + + off = vhd_sectors_to_bytes(blk + ctx->bm_secs); + size = vhd_sectors_to_bytes(ctx->spb); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + + err = vhd_write(ctx, data, size); + if (err) + return err; + + return 0; +} + +static inline int +namedup(char **dup, const char *name) +{ + *dup = NULL; + + if (strnlen(name, MAX_NAME_LEN) >= MAX_NAME_LEN) + return -ENAMETOOLONG; + + *dup = strdup(name); + if (*dup == NULL) + return -ENOMEM; + + return 0; +} + +int +vhd_seek(vhd_context_t *ctx, off_t offset, int whence) +{ + off_t off; + + off = lseek(ctx->fd, offset, whence); + if (off == (off_t)-1) { + VHDLOG("%s: seek(0x%08"PRIx64", %d) failed: %d\n", + ctx->file, offset, whence, -errno); + return -errno; + } + + return 0; +} + +off_t +vhd_position(vhd_context_t *ctx) +{ + return lseek(ctx->fd, 0, SEEK_CUR); +} + +int +vhd_read(vhd_context_t *ctx, void *buf, size_t size) +{ + size_t ret; + + errno = 0; + + ret = read(ctx->fd, buf, size); + if (ret == size) + return 0; + + VHDLOG("%s: read of %zu returned %zd, errno: %d\n", + ctx->file, size, ret, -errno); + + return (errno ? -errno : -EIO); +} + +int +vhd_write(vhd_context_t *ctx, void *buf, size_t size) +{ + size_t ret; + + errno = 0; + + ret = write(ctx->fd, buf, size); + if (ret == size) + return 0; + + VHDLOG("%s: write of %zu returned %zd, errno: %d\n", + ctx->file, size, ret, -errno); + + return (errno ? -errno : -EIO); +} + +int +vhd_offset(vhd_context_t *ctx, uint32_t sector, uint32_t *offset) +{ + int err; + uint32_t block; + + if (!vhd_type_dynamic(ctx)) + return sector; + + err = vhd_get_bat(ctx); + if (err) + return err; + + block = sector / ctx->spb; + if (ctx->bat.bat[block] == DD_BLK_UNUSED) + *offset = DD_BLK_UNUSED; + else + *offset = ctx->bat.bat[block] + + ctx->bm_secs + (sector % ctx->spb); + + return 0; +} + +int +vhd_open_fast(vhd_context_t *ctx) +{ + int err; + char *buf; + size_t size; + + size = sizeof(vhd_footer_t) + sizeof(vhd_header_t); + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + VHDLOG("failed allocating %s: %d\n", ctx->file, -err); + return -err; + } + + err = vhd_read(ctx, buf, size); + if (err) { + VHDLOG("failed reading %s: %d\n", ctx->file, err); + goto out; + } + + memcpy(&ctx->footer, buf, sizeof(vhd_footer_t)); + vhd_footer_in(&ctx->footer); + err = vhd_validate_footer(&ctx->footer); + if (err) + goto out; + + if (vhd_type_dynamic(ctx)) { + if (ctx->footer.data_offset != sizeof(vhd_footer_t)) + err = vhd_read_header(ctx, &ctx->header); + else { + memcpy(&ctx->header, + buf + sizeof(vhd_footer_t), + sizeof(vhd_header_t)); + vhd_header_in(&ctx->header); + err = vhd_validate_header(&ctx->header); + } + + if (err) + goto out; + + ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT; + ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3); + } + +out: + free(buf); + return err; +} + +int +vhd_open(vhd_context_t *ctx, const char *file, int flags) +{ + int err, oflags; + + if (flags & VHD_OPEN_STRICT) + vhd_flag_clear(flags, VHD_OPEN_FAST); + + memset(ctx, 0, sizeof(vhd_context_t)); + ctx->fd = -1; + ctx->oflags = flags; + + err = namedup(&ctx->file, file); + if (err) + return err; + + oflags = O_DIRECT | O_LARGEFILE; + if (flags & VHD_OPEN_RDONLY) + oflags |= O_RDONLY; + if (flags & VHD_OPEN_RDWR) + oflags |= O_RDWR; + + ctx->fd = open(ctx->file, oflags, 0644); + if (ctx->fd == -1) { + err = -errno; + VHDLOG("failed to open %s: %d\n", ctx->file, err); + goto fail; + } + + err = vhd_test_file_fixed(ctx->file, &ctx->is_block); + if (err) + goto fail; + + if (flags & VHD_OPEN_FAST) { + err = vhd_open_fast(ctx); + if (err) + goto fail; + + return 0; + } + + err = vhd_read_footer(ctx, &ctx->footer); + if (err) + goto fail; + + if (!(flags & VHD_OPEN_IGNORE_DISABLED) && vhd_disabled(ctx)) { + err = -EINVAL; + goto fail; + } + + if (vhd_type_dynamic(ctx)) { + err = vhd_read_header(ctx, &ctx->header); + if (err) + goto fail; + + ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT; + ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3); + } + + return 0; + +fail: + if (ctx->fd != -1) + close(ctx->fd); + free(ctx->file); + memset(ctx, 0, sizeof(vhd_context_t)); + return err; +} + +void +vhd_close(vhd_context_t *ctx) +{ + if (ctx->file) + close(ctx->fd); + free(ctx->file); + free(ctx->bat.bat); + free(ctx->batmap.map); + memset(ctx, 0, sizeof(vhd_context_t)); +} + +static inline void +vhd_initialize_footer(vhd_context_t *ctx, int type, uint64_t size) +{ + memset(&ctx->footer, 0, sizeof(vhd_footer_t)); + memcpy(ctx->footer.cookie, HD_COOKIE, sizeof(ctx->footer.cookie)); + ctx->footer.features = HD_RESERVED; + ctx->footer.ff_version = HD_FF_VERSION; + ctx->footer.timestamp = vhd_time(time(NULL)); + ctx->footer.crtr_ver = VHD_CURRENT_VERSION; + ctx->footer.crtr_os = 0x00000000; + ctx->footer.orig_size = size; + ctx->footer.curr_size = size; + ctx->footer.geometry = vhd_chs(size); + ctx->footer.type = type; + ctx->footer.saved = 0; + ctx->footer.data_offset = 0xFFFFFFFFFFFFFFFF; + strcpy(ctx->footer.crtr_app, "tap"); + blk_uuid_generate(&ctx->footer.uuid); +} + +static int +vhd_initialize_header_parent_name(vhd_context_t *ctx, const char *parent_path) +{ + int err; + iconv_t cd; + size_t ibl, obl; + char *ppath, *dst; + const char *pname; + + err = 0; + pname = NULL; + ppath = NULL; + + /* + * MICROSOFT_COMPAT + * big endian unicode here + */ + cd = iconv_open(UTF_16BE, "ASCII"); + if (cd == (iconv_t)-1) { + err = -errno; + goto out; + } + + ppath = strdup(parent_path); + if (!ppath) { + err = -ENOMEM; + goto out; + } + + pname = basename(ppath); + if (!strcmp(pname, "")) { + err = -EINVAL; + goto out; + } + + ibl = strlen(pname); + obl = sizeof(ctx->header.prt_name); + dst = ctx->header.prt_name; + + memset(dst, 0, obl); + + if (iconv(cd, +#ifdef __linux__ + (char **) +#endif + &pname, &ibl, &dst, &obl) == (size_t)-1 || ibl) + err = (errno ? -errno : -EINVAL); + +out: + iconv_close(cd); + free(ppath); + return err; +} + +static off_t +get_file_size(const char *name) +{ + int fd; + off_t end; + + fd = open(name, O_LARGEFILE | O_RDONLY); + if (fd == -1) { + VHDLOG("unable to open '%s': %d\n", name, errno); + return -errno; + } + end = lseek(fd, 0, SEEK_END); + close(fd); + return end; +} + +static int +vhd_initialize_header(vhd_context_t *ctx, const char *parent_path, + uint64_t size, int raw) +{ + int err; + struct stat stats; + vhd_context_t parent; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + memset(&ctx->header, 0, sizeof(vhd_header_t)); + memcpy(ctx->header.cookie, DD_COOKIE, sizeof(ctx->header.cookie)); + ctx->header.data_offset = (uint64_t)-1; + ctx->header.table_offset = VHD_SECTOR_SIZE * 3; /* 1 ftr + 2 hdr */ + ctx->header.hdr_ver = DD_VERSION; + ctx->header.block_size = VHD_BLOCK_SIZE; + ctx->header.prt_ts = 0; + ctx->header.res1 = 0; + ctx->header.max_bat_size = (ctx->footer.curr_size + + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT; + + ctx->footer.data_offset = VHD_SECTOR_SIZE; + + if (ctx->footer.type == HD_TYPE_DYNAMIC) + return 0; + + err = stat(parent_path, &stats); + if (err == -1) + return -errno; + + if (raw) { + ctx->header.prt_ts = vhd_time(stats.st_mtime); + if (!size) + size = get_file_size(parent_path); + } + else { + err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY); + if (err) + return err; + + ctx->header.prt_ts = vhd_time(stats.st_mtime); + blk_uuid_copy(&ctx->header.prt_uuid, &parent.footer.uuid); + if (!size) + size = parent.footer.curr_size; + vhd_close(&parent); + } + ctx->footer.orig_size = size; + ctx->footer.curr_size = size; + ctx->footer.geometry = vhd_chs(size); + ctx->header.max_bat_size = + (size + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT; + + return vhd_initialize_header_parent_name(ctx, parent_path); +} + +static int +vhd_write_parent_locators(vhd_context_t *ctx, const char *parent) +{ + int i, err; + off_t off; + uint32_t code; + + code = PLAT_CODE_NONE; + + if (ctx->footer.type != HD_TYPE_DIFF) + return -EINVAL; + + off = ctx->batmap.header.batmap_offset + + vhd_sectors_to_bytes(ctx->batmap.header.batmap_size); + if (off & (VHD_SECTOR_SIZE - 1)) + off = vhd_bytes_padded(off); + + for (i = 0; i < 3; i++) { + switch (i) { + case 0: + code = PLAT_CODE_MACX; + break; + case 1: + code = PLAT_CODE_W2KU; + break; + case 2: + code = PLAT_CODE_W2RU; + break; + } + + err = vhd_parent_locator_write_at(ctx, parent, off, code, + 0, ctx->header.loc + i); + if (err) + return err; + + off += vhd_parent_locator_size(ctx->header.loc + i); + } + + return 0; +} + +int +vhd_change_parent(vhd_context_t *child, char *parent_path, int raw) +{ + int i, err; + char *ppath; + struct stat stats; + vhd_context_t parent; + + ppath = realpath(parent_path, NULL); + if (!ppath) { + VHDLOG("error resolving parent path %s for %s: %d\n", + parent_path, child->file, errno); + return -errno; + } + + err = stat(ppath, &stats); + if (err == -1) { + err = -errno; + goto out; + } + + if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) { + err = -EINVAL; + goto out; + } + + if (raw) { + blk_uuid_clear(&child->header.prt_uuid); + } else { + err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY); + if (err) { + VHDLOG("error opening parent %s for %s: %d\n", + ppath, child->file, err); + goto out; + } + blk_uuid_copy(&child->header.prt_uuid, &parent.footer.uuid); + vhd_close(&parent); + } + + vhd_initialize_header_parent_name(child, ppath); + child->header.prt_ts = vhd_time(stats.st_mtime); + + for (i = 0; i < vhd_parent_locator_count(child); i++) { + vhd_parent_locator_t *loc = child->header.loc + i; + size_t max = vhd_parent_locator_size(loc); + + switch (loc->code) { + case PLAT_CODE_MACX: + case PLAT_CODE_W2KU: + case PLAT_CODE_W2RU: + break; + default: + continue; + } + + err = vhd_parent_locator_write_at(child, ppath, + loc->data_offset, + loc->code, max, loc); + if (err) { + VHDLOG("error writing parent locator %d for %s: %d\n", + i, child->file, err); + goto out; + } + } + + TEST_FAIL_AT(FAIL_REPARENT_LOCATOR); + + err = vhd_write_header(child, &child->header); + if (err) { + VHDLOG("error writing header for %s: %d\n", child->file, err); + goto out; + } + + err = 0; + +out: + free(ppath); + return err; +} + +static int +vhd_create_batmap(vhd_context_t *ctx) +{ + off_t off; + int err, map_bytes; + vhd_batmap_header_t *header; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + map_bytes = (ctx->header.max_bat_size + 7) >> 3; + header = &ctx->batmap.header; + + memset(header, 0, sizeof(vhd_batmap_header_t)); + memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(header->cookie)); + + err = vhd_batmap_header_offset(ctx, &off); + if (err) + return err; + + header->batmap_offset = off + + vhd_bytes_padded(sizeof(vhd_batmap_header_t)); + header->batmap_size = secs_round_up_no_zero(map_bytes); + header->batmap_version = VHD_BATMAP_CURRENT_VERSION; + + map_bytes = vhd_sectors_to_bytes(header->batmap_size); + + err = posix_memalign((void **)&ctx->batmap.map, + VHD_SECTOR_SIZE, map_bytes); + if (err) { + ctx->batmap.map = NULL; + return -err; + } + + memset(ctx->batmap.map, 0, map_bytes); + + return vhd_write_batmap(ctx, &ctx->batmap); +} + +static int +vhd_create_bat(vhd_context_t *ctx) +{ + int i, err; + size_t size; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t)); + err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size); + if (err) { + ctx->bat.bat = NULL; + return err; + } + + memset(ctx->bat.bat, 0, size); + for (i = 0; i < ctx->header.max_bat_size; i++) + ctx->bat.bat[i] = DD_BLK_UNUSED; + + err = vhd_seek(ctx, ctx->header.table_offset, SEEK_SET); + if (err) + return err; + + ctx->bat.entries = ctx->header.max_bat_size; + ctx->bat.spb = ctx->header.block_size >> VHD_SECTOR_SHIFT; + + return vhd_write_bat(ctx, &ctx->bat); +} + +static int +vhd_initialize_fixed_disk(vhd_context_t *ctx) +{ + char *buf; + int i, err; + + if (ctx->footer.type != HD_TYPE_FIXED) + return -EINVAL; + + err = vhd_seek(ctx, 0, SEEK_SET); + if (err) + return err; + + buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ, + MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) + return -errno; + + for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) { + err = vhd_write(ctx, buf, VHD_BLOCK_SIZE); + if (err) + goto out; + } + + err = 0; + +out: + munmap(buf, VHD_BLOCK_SIZE); + return err; +} + +int +vhd_get_phys_size(vhd_context_t *ctx, off_t *size) +{ + int err; + + if ((err = vhd_end_of_data(ctx, size))) + return err; + *size += sizeof(vhd_footer_t); + return 0; +} + +int +vhd_set_phys_size(vhd_context_t *ctx, off_t size) +{ + off_t phys_size; + int err; + + err = vhd_get_phys_size(ctx, &phys_size); + if (err) + return err; + if (size < phys_size) { + // would result in data loss + VHDLOG("ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n", + size, phys_size); + return -EINVAL; + } + return vhd_write_footer_at(ctx, &ctx->footer, + size - sizeof(vhd_footer_t)); +} + +static int +__vhd_create(const char *name, const char *parent, uint64_t bytes, int type, + vhd_flag_creat_t flags) +{ + int err; + off_t off; + vhd_context_t ctx; + vhd_footer_t *footer; + vhd_header_t *header; + uint64_t size, blks; + + switch (type) { + case HD_TYPE_DIFF: + if (!parent) + return -EINVAL; + case HD_TYPE_FIXED: + case HD_TYPE_DYNAMIC: + break; + default: + return -EINVAL; + } + + if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1) + return -ENAMETOOLONG; + + memset(&ctx, 0, sizeof(vhd_context_t)); + footer = &ctx.footer; + header = &ctx.header; + blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT; + size = blks << VHD_BLOCK_SHIFT; + + ctx.fd = open(name, O_WRONLY | O_CREAT | + O_TRUNC | O_LARGEFILE | O_DIRECT, 0644); + if (ctx.fd == -1) + return -errno; + + ctx.file = strdup(name); + if (!ctx.file) { + err = -ENOMEM; + goto out; + } + + err = vhd_test_file_fixed(ctx.file, &ctx.is_block); + if (err) + goto out; + + vhd_initialize_footer(&ctx, type, size); + + if (type == HD_TYPE_FIXED) { + err = vhd_initialize_fixed_disk(&ctx); + if (err) + goto out; + } else { + int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW); + err = vhd_initialize_header(&ctx, parent, size, raw); + if (err) + goto out; + + err = vhd_write_footer_at(&ctx, &ctx.footer, 0); + if (err) + goto out; + + err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE); + if (err) + goto out; + + err = vhd_create_batmap(&ctx); + if (err) + goto out; + + err = vhd_create_bat(&ctx); + if (err) + goto out; + + if (type == HD_TYPE_DIFF) { + err = vhd_write_parent_locators(&ctx, parent); + if (err) + goto out; + } + + /* write header again since it may have changed */ + err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE); + if (err) + goto out; + } + + err = vhd_seek(&ctx, 0, SEEK_END); + if (err) + goto out; + + off = vhd_position(&ctx); + if (off == (off_t)-1) { + err = -errno; + goto out; + } + + if (ctx.is_block) + off -= sizeof(vhd_footer_t); + + err = vhd_write_footer_at(&ctx, &ctx.footer, off); + if (err) + goto out; + + err = 0; + +out: + vhd_close(&ctx); + if (err && !ctx.is_block) + unlink(name); + return err; +} + +int +vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags) +{ + return __vhd_create(name, NULL, bytes, type, flags); +} + +int +vhd_snapshot(const char *name, uint64_t bytes, const char *parent, + vhd_flag_creat_t flags) +{ + return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags); +} + +static int +__vhd_io_fixed_read(vhd_context_t *ctx, + char *buf, uint64_t sec, uint32_t secs) +{ + int err; + + err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET); + if (err) + return err; + + return vhd_read(ctx, buf, vhd_sectors_to_bytes(secs)); +} + +static void +__vhd_io_dynamic_copy_data(vhd_context_t *ctx, + char *map, int map_off, + char *bitmap, int bitmap_off, + char *dst, char *src, int secs) +{ + int i; + + for (i = 0; i < secs; i++) { + if (test_bit(map, map_off + i)) + goto next; + + if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i)) + goto next; + + memcpy(dst, src, VHD_SECTOR_SIZE); + set_bit(map, map_off + i); + + next: + src += VHD_SECTOR_SIZE; + dst += VHD_SECTOR_SIZE; + } +} + +static int +__vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map, + char *buf, uint64_t sector, uint32_t secs) +{ + off_t off; + uint32_t blk, sec; + int err, cnt, map_off; + char *bitmap, *data, *src; + + map_off = 0; + + do { + blk = sector / ctx->spb; + sec = sector % ctx->spb; + off = ctx->bat.bat[blk]; + data = NULL; + bitmap = NULL; + + if (off == DD_BLK_UNUSED) { + cnt = MIN(secs, ctx->spb); + goto next; + } + + err = vhd_read_bitmap(ctx, blk, &bitmap); + if (err) + return err; + + err = vhd_read_block(ctx, blk, &data); + if (err) { + free(bitmap); + return err; + } + + cnt = MIN(secs, ctx->spb - sec); + src = data + vhd_sectors_to_bytes(sec); + + __vhd_io_dynamic_copy_data(ctx, + map, map_off, + bitmap, sec, + buf, src, cnt); + + next: + free(data); + free(bitmap); + + secs -= cnt; + sector += cnt; + map_off += cnt; + buf += vhd_sectors_to_bytes(cnt); + + } while (secs); + + return 0; +} + +static int +__raw_read_link(char *filename, + char *map, char *buf, uint64_t sec, uint32_t secs) +{ + int fd, err; + off_t off; + uint64_t size; + char *data; + + err = 0; + errno = 0; + fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE); + if (fd == -1) { + VHDLOG("%s: failed to open: %d\n", filename, -errno); + return -errno; + } + + off = lseek(fd, vhd_sectors_to_bytes(sec), SEEK_SET); + if (off == (off_t)-1) { + VHDLOG("%s: seek(0x%08"PRIx64") failed: %d\n", + filename, vhd_sectors_to_bytes(sec), -errno); + err = -errno; + goto close; + } + + size = vhd_sectors_to_bytes(secs); + err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size); + if (err) + goto close; + + err = read(fd, data, size); + if (err != size) { + VHDLOG("%s: reading of %"PRIu64" returned %d, errno: %d\n", + filename, size, err, -errno); + free(data); + err = errno ? -errno : -EIO; + goto close; + } + __vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs); + free(data); + err = 0; + +close: + close(fd); + return err; +} + +static int +__vhd_io_dynamic_read(vhd_context_t *ctx, + char *buf, uint64_t sec, uint32_t secs) +{ + int err; + uint32_t i, done; + char *map, *next; + vhd_context_t parent, *vhd; + + err = vhd_get_bat(ctx); + if (err) + return err; + + vhd = ctx; + next = NULL; + map = calloc(1, secs << (VHD_SECTOR_SHIFT - 3)); + if (!map) + return -ENOMEM; + + memset(buf, 0, vhd_sectors_to_bytes(secs)); + + for (;;) { + err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs); + if (err) + goto close; + + for (done = 0, i = 0; i < secs; i++) + if (test_bit(map, i)) + done++; + + if (done == secs) { + err = 0; + goto close; + } + + if (vhd->footer.type == HD_TYPE_DIFF) { + err = vhd_parent_locator_get(vhd, &next); + if (err) + goto close; + if (vhd_parent_raw(vhd)) { + err = __raw_read_link(next, map, buf, sec, + secs); + goto close; + } + } else { + err = 0; + goto close; + } + + if (vhd != ctx) + vhd_close(vhd); + vhd = &parent; + + err = vhd_open(vhd, next, VHD_OPEN_RDONLY); + if (err) + goto out; + + err = vhd_get_bat(vhd); + if (err) + goto close; + + free(next); + next = NULL; + } + +close: + if (vhd != ctx) + vhd_close(vhd); +out: + free(map); + free(next); + return err; +} + +int +vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs) +{ + if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size) + return -ERANGE; + + if (!vhd_type_dynamic(ctx)) + return __vhd_io_fixed_read(ctx, buf, sec, secs); + + return __vhd_io_dynamic_read(ctx, buf, sec, secs); +} + +static int +__vhd_io_fixed_write(vhd_context_t *ctx, + char *buf, uint64_t sec, uint32_t secs) +{ + int err; + + err = vhd_seek(ctx, vhd_sectors_to_bytes(sec), SEEK_SET); + if (err) + return err; + + return vhd_write(ctx, buf, vhd_sectors_to_bytes(secs)); +} + +static int +__vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block) +{ + char *buf; + size_t size; + off_t off, max; + int i, err, gap, spp; + + spp = getpagesize() >> VHD_SECTOR_SHIFT; + + err = vhd_end_of_data(ctx, &max); + if (err) + return err; + + gap = 0; + off = max; + max >>= VHD_SECTOR_SHIFT; + + /* data region of segment should begin on page boundary */ + if ((max + ctx->bm_secs) % spp) { + gap = (spp - ((max + ctx->bm_secs) % spp)); + max += gap; + } + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + + size = vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap); + buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) + return -errno; + + err = vhd_write(ctx, buf, size); + if (err) + goto out; + + ctx->bat.bat[block] = max; + err = vhd_write_bat(ctx, &ctx->bat); + if (err) + goto out; + + err = 0; + +out: + munmap(buf, size); + return err; +} + +static int +__vhd_io_dynamic_write(vhd_context_t *ctx, + char *buf, uint64_t sector, uint32_t secs) +{ + char *map; + off_t off; + uint32_t blk, sec; + int i, err, cnt, ret; + + if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size) + return -ERANGE; + + err = vhd_get_bat(ctx); + if (err) + return err; + + if (vhd_has_batmap(ctx)) { + err = vhd_get_batmap(ctx); + if (err) + return err; + } + + do { + blk = sector / ctx->spb; + sec = sector % ctx->spb; + + off = ctx->bat.bat[blk]; + if (off == DD_BLK_UNUSED) { + err = __vhd_io_allocate_block(ctx, blk); + if (err) + return err; + + off = ctx->bat.bat[blk]; + } + + off += ctx->bm_secs + sec; + err = vhd_seek(ctx, vhd_sectors_to_bytes(off), SEEK_SET); + if (err) + return err; + + cnt = MIN(secs, ctx->spb - sec); + err = vhd_write(ctx, buf, vhd_sectors_to_bytes(cnt)); + if (err) + return err; + + if (vhd_has_batmap(ctx) && + vhd_batmap_test(ctx, &ctx->batmap, blk)) + goto next; + + err = vhd_read_bitmap(ctx, blk, &map); + if (err) + return err; + + for (i = 0; i < cnt; i++) + vhd_bitmap_set(ctx, map, sec + i); + + err = vhd_write_bitmap(ctx, blk, map); + if (err) + goto fail; + + if (vhd_has_batmap(ctx)) { + for (i = 0; i < ctx->spb; i++) + if (!vhd_bitmap_test(ctx, map, i)) { + free(map); + goto next; + } + + vhd_batmap_set(ctx, &ctx->batmap, blk); + err = vhd_write_batmap(ctx, &ctx->batmap); + if (err) + goto fail; + } + + free(map); + map = NULL; + + next: + secs -= cnt; + sector += cnt; + buf += vhd_sectors_to_bytes(cnt); + } while (secs); + + err = 0; + +out: + ret = vhd_write_footer(ctx, &ctx->footer); + return (err ? err : ret); + +fail: + free(map); + goto out; +} + +int +vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs) +{ + if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size) + return -ERANGE; + + if (!vhd_type_dynamic(ctx)) + return __vhd_io_fixed_write(ctx, buf, sec, secs); + + return __vhd_io_dynamic_write(ctx, buf, sec, secs); +} diff --git a/tools/vhd-tools/vhd/lib/relative-path.c b/tools/vhd-tools/vhd/lib/relative-path.c new file mode 100644 index 00000000000..8b7cb71fc9c --- /dev/null +++ b/tools/vhd-tools/vhd/lib/relative-path.c @@ -0,0 +1,299 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include + +#include "relative-path.h" + +#define sfree(ptr) \ +do { \ + free(ptr); \ + ptr = NULL; \ +} while (0) + +/* + * count number of tokens between DELIMETER characters + */ +static int +count_nodes(char *path) +{ + int i; + char *tmp; + + if (!path) + return 0; + + for (i = 0, tmp = path; *tmp != '\0'; tmp++) + if (*tmp == DELIMITER) + i++; + + return i; +} + +/* + * return copy of next node in @path, or NULL + * @path is moved to the end of the next node + * @err is set to -errno on failure + * copy should be freed + */ +static char * +next_node(char **path, int *err) +{ + int ret; + char *tmp, *start; + + if (!path || !*path) { + *err = -EINVAL; + return NULL; + } + + *err = 0; + start = *path; + + for (tmp = *path; *tmp != '\0'; tmp++) + if (*tmp == DELIMITER) { + int size; + char *node; + + size = tmp - start + 1; + node = malloc(size); + if (!node) { + *err = -ENOMEM; + return NULL; + } + + ret = snprintf(node, size, "%s", start); + if (ret < 0) { + free(node); + *err = -EINVAL; + return NULL; + } + + *path = tmp; + return node; + } + + return NULL; +} + +/* + * count number of nodes in common betwee @to and @from + * returns number of common nodes, or -errno on failure + */ +static int +count_common_nodes(char *to, char *from) +{ + int err, common; + char *to_node, *from_node; + + if (!to || !from) + return -EINVAL; + + err = 0; + common = 0; + to_node = NULL; + from_node = NULL; + + do { + to_node = next_node(&to, &err); + if (err || !to_node) + break; + + from_node = next_node(&from, &err); + if (err || !from_node) + break; + + if (strncmp(to_node, from_node, MAX_NAME_LEN)) + break; + + ++to; + ++from; + ++common; + sfree(to_node); + sfree(from_node); + + } while (1); + + sfree(to_node); + sfree(from_node); + + if (err) + return err; + + return common; +} + +/* + * construct path of @count '../', './' if @count is zero, or NULL on error + * result should be freed + */ +static char * +up_nodes(int count) +{ + char *path, *tmp; + int i, ret, len, size; + + if (!count) + return strdup("./"); + + len = strlen("../"); + size = len * count; + if (size >= MAX_NAME_LEN) + return NULL; + + path = malloc(size + 1); + if (!path) + return NULL; + + tmp = path; + for (i = 0; i < count; i++) { + ret = sprintf(tmp, "../"); + if (ret < 0 || ret != len) { + free(path); + return NULL; + } + tmp += ret; + } + + return path; +} + +/* + * return pointer to @offset'th node of path or NULL on error + */ +static char * +node_offset(char *from, int offset) +{ + char *path; + + if (!from || !offset) + return NULL; + + for (path = from; *path != '\0'; path++) { + if (*path == DELIMITER) + if (--offset == 0) + return path + 1; + } + + return NULL; +} + +/* + * return a relative path from @from to @to + * result should be freed + */ +char * +relative_path_to(char *from, char *to, int *err) +{ + int from_nodes, common; + char *to_absolute, *from_absolute; + char *up, *common_target_path, *relative_path; + + *err = 0; + up = NULL; + to_absolute = NULL; + from_absolute = NULL; + relative_path = NULL; + + if (strnlen(to, MAX_NAME_LEN) == MAX_NAME_LEN || + strnlen(from, MAX_NAME_LEN) == MAX_NAME_LEN) { + EPRINTF("invalid input; max path length is %d\n", + MAX_NAME_LEN); + *err = -ENAMETOOLONG; + return NULL; + } + + to_absolute = realpath(to, NULL); + if (!to_absolute) { + EPRINTF("failed to get absolute path of %s\n", to); + *err = -errno; + goto out; + } + + from_absolute = realpath(from, NULL); + if (!from_absolute) { + EPRINTF("failed to get absolute path of %s\n", from); + *err = -errno; + goto out; + } + + if (strnlen(to_absolute, MAX_NAME_LEN) == MAX_NAME_LEN || + strnlen(from_absolute, MAX_NAME_LEN) == MAX_NAME_LEN) { + EPRINTF("invalid input; max path length is %d\n", + MAX_NAME_LEN); + *err = -ENAMETOOLONG; + goto out; + } + + /* count nodes in source path */ + from_nodes = count_nodes(from_absolute); + + /* count nodes in common */ + common = count_common_nodes(to_absolute + 1, from_absolute + 1); + if (common < 0) { + EPRINTF("failed to count common nodes of %s and %s: %d\n", + to_absolute, from_absolute, common); + *err = common; + goto out; + } + + /* move up to common node */ + up = up_nodes(from_nodes - common - 1); + if (!up) { + EPRINTF("failed to allocate relative path for %s: %d\n", + from_absolute, -ENOMEM); + *err = -ENOMEM; + goto out; + } + + /* get path from common node to target */ + common_target_path = node_offset(to_absolute, common + 1); + if (!common_target_path) { + EPRINTF("failed to find common target path to %s: %d\n", + to_absolute, -EINVAL); + *err = -EINVAL; + goto out; + } + + /* get relative path */ + if (asprintf(&relative_path, "%s%s", up, common_target_path) == -1) { + EPRINTF("failed to construct final path %s%s: %d\n", + up, common_target_path, -ENOMEM); + relative_path = NULL; + *err = -ENOMEM; + goto out; + } + +out: + sfree(up); + sfree(to_absolute); + sfree(from_absolute); + + return relative_path; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-check.c b/tools/vhd-tools/vhd/lib/vhd-util-check.c new file mode 100644 index 00000000000..59a1a473293 --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-check.c @@ -0,0 +1,980 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libvhd.h" +#include "vhd-util.h" + +// allow the VHD timestamp to be at most this many seconds into the future to +// account for time skew with NFS servers +#define TIMESTAMP_MAX_SLACK 1800 + +static int +vhd_util_check_zeros(void *buf, size_t size) +{ + int i; + char *p; + + p = buf; + for (i = 0; i < size; i++) + if (p[i]) + return i; + + return 0; +} + +static int +vhd_util_check_footer_opened(vhd_footer_t *footer) +{ + int i, n; + uint32_t *buf; + + buf = (uint32_t *)footer; + n = sizeof(*footer) / sizeof(uint32_t); + + for (i = 0; i < n; i++) + if (buf[i] != 0xc7c7c7c7) + return 0; + + return 1; +} + +static char * +vhd_util_check_validate_footer(vhd_footer_t *footer) +{ + int size; + uint32_t checksum, now; + + size = sizeof(footer->cookie); + if (memcmp(footer->cookie, HD_COOKIE, size)) + return "invalid cookie"; + + checksum = vhd_checksum_footer(footer); + if (checksum != footer->checksum) { + if (footer->hidden && + !strncmp(footer->crtr_app, "tap", 3) && + (footer->crtr_ver == VHD_VERSION(0, 1) || + footer->crtr_ver == VHD_VERSION(1, 1))) { + char tmp = footer->hidden; + footer->hidden = 0; + checksum = vhd_checksum_footer(footer); + footer->hidden = tmp; + + if (checksum == footer->checksum) + goto ok; + } + + return "invalid checksum"; + } + +ok: + if (!(footer->features & HD_RESERVED)) + return "invalid 'reserved' feature"; + + if (footer->features & ~(HD_TEMPORARY | HD_RESERVED)) + return "invalid extra features"; + + if (footer->ff_version != HD_FF_VERSION) + return "invalid file format version"; + + if (footer->type != HD_TYPE_DYNAMIC && + footer->type != HD_TYPE_DIFF && + footer->data_offset != ~(0ULL)) + return "invalid data offset"; + + now = vhd_time(time(NULL)); + if (footer->timestamp > now + TIMESTAMP_MAX_SLACK) + return "creation time in future"; + + if (!strncmp(footer->crtr_app, "tap", 3) && + footer->crtr_ver > VHD_CURRENT_VERSION) + return "unsupported tap creator version"; + + if (vhd_chs(footer->curr_size) < footer->geometry) + return "geometry too large"; + + if (footer->type != HD_TYPE_FIXED && + footer->type != HD_TYPE_DYNAMIC && + footer->type != HD_TYPE_DIFF) + return "invalid type"; + + if (footer->saved && footer->saved != 1) + return "invalid 'saved' state"; + + if (footer->hidden && footer->hidden != 1) + return "invalid 'hidden' state"; + + if (vhd_util_check_zeros(footer->reserved, + sizeof(footer->reserved))) + return "invalid 'reserved' bits"; + + return NULL; +} + +static char * +vhd_util_check_validate_header(int fd, vhd_header_t *header) +{ + off_t eof; + int i, cnt, size; + uint32_t checksum; + + size = sizeof(header->cookie); + if (memcmp(header->cookie, DD_COOKIE, size)) + return "invalid cookie"; + + checksum = vhd_checksum_header(header); + if (checksum != header->checksum) + return "invalid checksum"; + + if (header->hdr_ver != 0x00010000) + return "invalid header version"; + + if (header->data_offset != ~(0ULL)) + return "invalid data offset"; + + eof = lseek(fd, 0, SEEK_END); + if (eof == (off_t)-1) + return "error finding eof"; + + if (header->table_offset <= 0 || + header->table_offset % 512 || + (header->table_offset + + (header->max_bat_size * sizeof(uint32_t)) > + eof - sizeof(vhd_footer_t))) + return "invalid table offset"; + + for (cnt = 0, i = 0; i < sizeof(header->block_size) * 8; i++) + if ((header->block_size >> i) & 1) + cnt++; + + if (cnt != 1) + return "invalid block size"; + + if (header->res1) + return "invalid reserved bits"; + + if (vhd_util_check_zeros(header->res2, sizeof(header->res2))) + return "invalid reserved bits"; + + return NULL; +} + +static char * +vhd_util_check_validate_differencing_header(vhd_context_t *vhd) +{ + vhd_header_t *header; + + header = &vhd->header; + + if (vhd->footer.type == HD_TYPE_DIFF) { + char *parent; + uint32_t now; + + now = vhd_time(time(NULL)); + if (header->prt_ts > now + TIMESTAMP_MAX_SLACK) + return "parent creation time in future"; + + if (vhd_header_decode_parent(vhd, header, &parent)) + return "invalid parent name"; + + free(parent); + } else { + if (vhd_util_check_zeros(header->prt_name, + sizeof(header->prt_name))) + return "invalid non-null parent name"; + + if (vhd_util_check_zeros(header->loc, sizeof(header->loc))) + return "invalid non-null parent locators"; + + if (!blk_uuid_is_nil(&header->prt_uuid)) + return "invalid non-null parent uuid"; + + if (header->prt_ts) + return "invalid non-zero parent timestamp"; + } + + return NULL; +} + +static char * +vhd_util_check_validate_batmap(vhd_context_t *vhd, vhd_batmap_t *batmap) +{ + int size; + off_t eof; + uint32_t checksum; + + size = sizeof(batmap->header.cookie); + if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, size)) + return "invalid cookie"; + + if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION) + return "unsupported batmap version"; + + checksum = vhd_checksum_batmap(batmap); + if (checksum != batmap->header.checksum) + return "invalid checksum"; + + if (!batmap->header.batmap_size) + return "invalid size zero"; + + eof = lseek(vhd->fd, 0, SEEK_END); + if (eof == (off_t)-1) + return "error finding eof"; + + if (!batmap->header.batmap_offset || + batmap->header.batmap_offset % 512) + return "invalid batmap offset"; + + if ((batmap->header.batmap_offset + + vhd_sectors_to_bytes(batmap->header.batmap_size)) > + eof - sizeof(vhd_footer_t)) + return "invalid batmap size"; + + return NULL; +} + +static char * +vhd_util_check_validate_parent_locator(vhd_context_t *vhd, + vhd_parent_locator_t *loc) +{ + off_t eof; + + if (vhd_validate_platform_code(loc->code)) + return "invalid platform code"; + + if (loc->code == PLAT_CODE_NONE) { + if (vhd_util_check_zeros(loc, sizeof(*loc))) + return "non-zero locator"; + + return NULL; + } + + if (!loc->data_offset) + return "invalid data offset"; + + if (!loc->data_space) + return "invalid data space"; + + if (!loc->data_len) + return "invalid data length"; + + eof = lseek(vhd->fd, 0, SEEK_END); + if (eof == (off_t)-1) + return "error finding eof"; + + if (loc->data_offset + vhd_parent_locator_size(loc) > + eof - sizeof(vhd_footer_t)) + return "invalid size"; + + if (loc->res) + return "invalid reserved bits"; + + return NULL; +} + +static const char * +vhd_util_check_validate_parent(vhd_context_t *vhd, const char *ppath) +{ + const char *msg; + vhd_context_t parent; + uint32_t status; + + msg = NULL; + + if (vhd_parent_raw(vhd)) + return msg; + + if (vhd_open(&parent, ppath, + VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED)) + return "error opening parent"; + + if (blk_uuid_compare(&vhd->header.prt_uuid, &parent.footer.uuid)) { + msg = "invalid parent uuid"; + goto out; + } + +out: + vhd_close(&parent); + return msg; +} + +static int +vhd_util_check_footer(int fd, vhd_footer_t *footer, int ignore) +{ + size_t size; + int err, opened; + char *msg, *buf; + off_t eof, off; + vhd_footer_t primary, backup; + + memset(&primary, 0, sizeof(primary)); + memset(&backup, 0, sizeof(backup)); + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(primary)); + if (err) { + printf("error allocating buffer: %d\n", err); + return -err; + } + + memset(buf, 0, sizeof(primary)); + + eof = lseek(fd, 0, SEEK_END); + if (eof == (off_t)-1) { + err = -errno; + printf("error calculating end of file: %d\n", err); + goto out; + } + + size = ((eof % 512) ? 511 : 512); + eof = lseek(fd, eof - size, SEEK_SET); + if (eof == (off_t)-1) { + err = -errno; + printf("error calculating end of file: %d\n", err); + goto out; + } + + err = read(fd, buf, 512); + if (err != size) { + err = (errno ? -errno : -EIO); + printf("error reading primary footer: %d\n", err); + goto out; + } + + memcpy(&primary, buf, sizeof(primary)); + opened = vhd_util_check_footer_opened(&primary); + vhd_footer_in(&primary); + + msg = vhd_util_check_validate_footer(&primary); + if (msg) { + if (opened && ignore) + goto check_backup; + + err = -EINVAL; + printf("primary footer invalid: %s\n", msg); + goto out; + } + + if (primary.type == HD_TYPE_FIXED) { + err = 0; + goto out; + } + +check_backup: + off = lseek(fd, 0, SEEK_SET); + if (off == (off_t)-1) { + err = -errno; + printf("error seeking to backup footer: %d\n", err); + goto out; + } + + size = 512; + memset(buf, 0, sizeof(primary)); + + err = read(fd, buf, size); + if (err != size) { + err = (errno ? -errno : -EIO); + printf("error reading backup footer: %d\n", err); + goto out; + } + + memcpy(&backup, buf, sizeof(backup)); + vhd_footer_in(&backup); + + msg = vhd_util_check_validate_footer(&backup); + if (msg) { + err = -EINVAL; + printf("backup footer invalid: %s\n", msg); + goto out; + } + + if (memcmp(&primary, &backup, sizeof(primary))) { + if (opened && ignore) { + memcpy(&primary, &backup, sizeof(primary)); + goto ok; + } + + if (backup.hidden && + !strncmp(backup.crtr_app, "tap", 3) && + (backup.crtr_ver == VHD_VERSION(0, 1) || + backup.crtr_ver == VHD_VERSION(1, 1))) { + char cmp, tmp = backup.hidden; + backup.hidden = 0; + cmp = memcmp(&primary, &backup, sizeof(primary)); + backup.hidden = tmp; + if (!cmp) + goto ok; + } + + err = -EINVAL; + printf("primary and backup footers do not match\n"); + goto out; + } + +ok: + err = 0; + memcpy(footer, &primary, sizeof(primary)); + +out: + free(buf); + return err; +} + +static int +vhd_util_check_header(int fd, vhd_footer_t *footer) +{ + int err; + off_t off; + char *msg, *buf; + vhd_header_t header; + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(header)); + if (err) { + printf("error allocating header: %d\n", err); + return err; + } + + off = footer->data_offset; + off = lseek(fd, off, SEEK_SET); + if (off == (off_t)-1) { + err = -errno; + printf("error seeking to header: %d\n", err); + goto out; + } + + err = read(fd, buf, sizeof(header)); + if (err != sizeof(header)) { + err = (errno ? -errno : -EIO); + printf("error reading header: %d\n", err); + goto out; + } + + memcpy(&header, buf, sizeof(header)); + vhd_header_in(&header); + + msg = vhd_util_check_validate_header(fd, &header); + if (msg) { + err = -EINVAL; + printf("header is invalid: %s\n", msg); + goto out; + } + + err = 0; + +out: + free(buf); + return err; +} + +static int +vhd_util_check_differencing_header(vhd_context_t *vhd) +{ + char *msg; + + msg = vhd_util_check_validate_differencing_header(vhd); + if (msg) { + printf("differencing header is invalid: %s\n", msg); + return -EINVAL; + } + + return 0; +} + +static int +vhd_util_check_bat(vhd_context_t *vhd) +{ + off_t eof, eoh; + int i, j, err, block_size; + + err = vhd_seek(vhd, 0, SEEK_END); + if (err) { + printf("error calculating eof: %d\n", err); + return err; + } + + eof = vhd_position(vhd); + if (eof == (off_t)-1) { + printf("error calculating eof: %d\n", -errno); + return -errno; + } + + /* adjust eof for vhds with short footers */ + if (eof % 512) { + if (eof % 512 != 511) { + printf("invalid file size: 0x%"PRIx64"\n", eof); + return -EINVAL; + } + + eof++; + } + + err = vhd_get_bat(vhd); + if (err) { + printf("error reading bat: %d\n", err); + return err; + } + + err = vhd_end_of_headers(vhd, &eoh); + if (err) { + printf("error calculating end of metadata: %d\n", err); + return err; + } + + eof -= sizeof(vhd_footer_t); + eof >>= VHD_SECTOR_SHIFT; + eoh >>= VHD_SECTOR_SHIFT; + block_size = vhd->spb + vhd->bm_secs; + + for (i = 0; i < vhd->header.max_bat_size; i++) { + uint32_t off = vhd->bat.bat[i]; + if (off == DD_BLK_UNUSED) + continue; + + if (off < eoh) { + printf("block %d (offset 0x%x) clobbers headers\n", + i, off); + return -EINVAL; + } + + if (off + block_size > eof) { + printf("block %d (offset 0x%x) clobbers footer\n", + i, off); + return -EINVAL; + } + + for (j = 0; j < vhd->header.max_bat_size; j++) { + uint32_t joff = vhd->bat.bat[j]; + + if (i == j) + continue; + + if (joff == DD_BLK_UNUSED) + continue; + + if (off == joff) + err = -EINVAL; + + if (off > joff && off < joff + block_size) + err = -EINVAL; + + if (off + block_size > joff && + off + block_size < joff + block_size) + err = -EINVAL; + + if (err) { + printf("block %d (offset 0x%x) clobbers " + "block %d (offset 0x%x)\n", + i, off, j, joff); + return err; + } + } + } + + return 0; +} + +static int +vhd_util_check_batmap(vhd_context_t *vhd) +{ + char *msg; + int i, err; + + err = vhd_get_bat(vhd); + if (err) { + printf("error reading bat: %d\n", err); + return err; + } + + err = vhd_get_batmap(vhd); + if (err) { + printf("error reading batmap: %d\n", err); + return err; + } + + msg = vhd_util_check_validate_batmap(vhd, &vhd->batmap); + if (msg) { + printf("batmap is invalid: %s\n", msg); + return -EINVAL; + } + + for (i = 0; i < vhd->header.max_bat_size; i++) { + if (!vhd_batmap_test(vhd, &vhd->batmap, i)) + continue; + + if (vhd->bat.bat[i] == DD_BLK_UNUSED) { + printf("batmap shows unallocated block %d full\n", i); + return -EINVAL; + } + } + + return 0; +} + +static int +vhd_util_check_parent_locators(vhd_context_t *vhd) +{ + int i, n, err; + vhd_parent_locator_t *loc; + char *file, *ppath, *location, *pname; + const char *msg; + int mac, macx, w2ku, w2ru, wi2r, wi2k, found; + + mac = 0; + macx = 0; + w2ku = 0; + w2ru = 0; + wi2r = 0; + wi2k = 0; + found = 0; + pname = NULL; + ppath = NULL; + location = NULL; + + err = vhd_header_decode_parent(vhd, &vhd->header, &pname); + if (err) { + printf("error decoding parent name: %d\n", err); + return err; + } + + n = sizeof(vhd->header.loc) / sizeof(vhd->header.loc[0]); + for (i = 0; i < n; i++) { + ppath = NULL; + location = NULL; + loc = vhd->header.loc + i; + + msg = vhd_util_check_validate_parent_locator(vhd, loc); + if (msg) { + err = -EINVAL; + printf("invalid parent locator %d: %s\n", i, msg); + goto out; + } + + if (loc->code == PLAT_CODE_NONE) + continue; + + switch (loc->code) { + case PLAT_CODE_MACX: + if (macx++) + goto dup; + break; + + case PLAT_CODE_MAC: + if (mac++) + goto dup; + break; + + case PLAT_CODE_W2KU: + if (w2ku++) + goto dup; + break; + + case PLAT_CODE_W2RU: + if (w2ru++) + goto dup; + break; + + case PLAT_CODE_WI2R: + if (wi2r++) + goto dup; + break; + + case PLAT_CODE_WI2K: + if (wi2k++) + goto dup; + break; + + default: + err = -EINVAL; + printf("invalid platform code for locator %d\n", i); + goto out; + } + + if (loc->code != PLAT_CODE_MACX && + loc->code != PLAT_CODE_W2RU && + loc->code != PLAT_CODE_W2KU) + continue; + + err = vhd_parent_locator_read(vhd, loc, &ppath); + if (err) { + printf("error reading parent locator %d: %d\n", i, err); + goto out; + } + + file = basename(ppath); + if (strcmp(pname, file)) { + err = -EINVAL; + printf("parent locator %d name (%s) does not match " + "header name (%s)\n", i, file, pname); + goto out; + } + + err = vhd_find_parent(vhd, ppath, &location); + if (err) { + printf("error resolving %s: %d\n", ppath, err); + goto out; + } + + err = access(location, R_OK); + if (err && loc->code == PLAT_CODE_MACX) { + err = -errno; + printf("parent locator %d points to missing file %s " + "(resolved to %s)\n", i, ppath, location); + goto out; + } + + msg = vhd_util_check_validate_parent(vhd, location); + if (msg) { + err = -EINVAL; + printf("invalid parent %s: %s\n", location, msg); + goto out; + } + + found++; + free(ppath); + free(location); + ppath = NULL; + location = NULL; + + continue; + + dup: + printf("duplicate platform code in locator %d: 0x%x\n", + i, loc->code); + err = -EINVAL; + goto out; + } + + if (!found) { + err = -EINVAL; + printf("could not find parent %s\n", pname); + goto out; + } + + err = 0; + +out: + free(pname); + free(ppath); + free(location); + return err; +} + +static void +vhd_util_dump_headers(const char *name) +{ + char *argv[] = { "read", "-p", "-n", (char *)name }; + int argc = sizeof(argv) / sizeof(argv[0]); + + printf("%s appears invalid; dumping metadata\n", name); + vhd_util_read(argc, argv); +} + +static int +vhd_util_check_vhd(const char *name, int ignore) +{ + int fd, err; + vhd_context_t vhd; + struct stat stats; + vhd_footer_t footer; + + fd = -1; + memset(&vhd, 0, sizeof(vhd)); + memset(&footer, 0, sizeof(footer)); + + err = stat(name, &stats); + if (err == -1) { + printf("cannot stat %s: %d\n", name, errno); + return -errno; + } + + if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) { + printf("%s is not a regular file or block device\n", name); + return -EINVAL; + } + + fd = open(name, O_RDONLY | O_DIRECT | O_LARGEFILE); + if (fd == -1) { + printf("error opening %s\n", name); + return -errno; + } + + err = vhd_util_check_footer(fd, &footer, ignore); + if (err) + goto out; + + if (footer.type != HD_TYPE_DYNAMIC && footer.type != HD_TYPE_DIFF) + goto out; + + err = vhd_util_check_header(fd, &footer); + if (err) + goto out; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED); + if (err) + goto out; + + err = vhd_util_check_differencing_header(&vhd); + if (err) + goto out; + + err = vhd_util_check_bat(&vhd); + if (err) + goto out; + + if (vhd_has_batmap(&vhd)) { + err = vhd_util_check_batmap(&vhd); + if (err) + goto out; + } + + if (vhd.footer.type == HD_TYPE_DIFF) { + err = vhd_util_check_parent_locators(&vhd); + if (err) + goto out; + } + + err = 0; + printf("%s is valid\n", name); + +out: + if (err) + vhd_util_dump_headers(name); + if (fd != -1) + close(fd); + vhd_close(&vhd); + return err; +} + +static int +vhd_util_check_parents(const char *name, int ignore) +{ + int err; + vhd_context_t vhd; + char *cur, *parent; + + cur = (char *)name; + + for (;;) { + err = vhd_open(&vhd, cur, + VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED); + if (err) + goto out; + + if (vhd.footer.type != HD_TYPE_DIFF || vhd_parent_raw(&vhd)) { + vhd_close(&vhd); + goto out; + } + + err = vhd_parent_locator_get(&vhd, &parent); + vhd_close(&vhd); + + if (err) { + printf("error getting parent: %d\n", err); + goto out; + } + + if (cur != name) + free(cur); + cur = parent; + + err = vhd_util_check_vhd(cur, ignore); + if (err) + goto out; + } + +out: + if (err) + printf("error checking parents: %d\n", err); + if (cur != name) + free(cur); + return err; +} + +int +vhd_util_check(int argc, char **argv) +{ + char *name; + vhd_context_t vhd; + int c, err, ignore, parents; + + if (!argc || !argv) { + err = -EINVAL; + goto usage; + } + + ignore = 0; + parents = 0; + name = NULL; + + optind = 0; + while ((c = getopt(argc, argv, "n:iph")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'i': + ignore = 1; + break; + case 'p': + parents = 1; + break; + case 'h': + err = 0; + goto usage; + default: + err = -EINVAL; + goto usage; + } + } + + if (!name || optind != argc) { + err = -EINVAL; + goto usage; + } + + err = vhd_util_check_vhd(name, ignore); + if (err) + goto out; + + if (parents) + err = vhd_util_check_parents(name, ignore); + +out: + return err; + +usage: + printf("options: -n [-i ignore missing primary footers] " + "[-p check parents] [-h help]\n"); + return err; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-coalesce.c b/tools/vhd-tools/vhd/lib/vhd-util-coalesce.c new file mode 100644 index 00000000000..63dcf6010e0 --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-coalesce.c @@ -0,0 +1,218 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include + +#include "libvhd.h" + +static int +__raw_io_write(int fd, char* buf, uint64_t sec, uint32_t secs) +{ + off_t off; + size_t ret; + + errno = 0; + off = lseek(fd, vhd_sectors_to_bytes(sec), SEEK_SET); + if (off == (off_t)-1) { + printf("raw parent: seek(0x%08"PRIx64") failed: %d\n", + vhd_sectors_to_bytes(sec), -errno); + return -errno; + } + + ret = write(fd, buf, vhd_sectors_to_bytes(secs)); + if (ret == vhd_sectors_to_bytes(secs)) + return 0; + + printf("raw parent: write of 0x%"PRIx64" returned %zd, errno: %d\n", + vhd_sectors_to_bytes(secs), ret, -errno); + return (errno ? -errno : -EIO); +} + +/* + * Use 'parent' if the parent is VHD, and 'parent_fd' if the parent is raw + */ +static int +vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent, + int parent_fd, uint64_t block) +{ + int i, err; + char *buf, *map; + uint64_t sec, secs; + + buf = NULL; + map = NULL; + sec = block * vhd->spb; + + if (vhd->bat.bat[block] == DD_BLK_UNUSED) + return 0; + + err = posix_memalign((void **)&buf, 4096, vhd->header.block_size); + if (err) + return -err; + + err = vhd_io_read(vhd, buf, sec, vhd->spb); + if (err) + goto done; + + if (vhd_has_batmap(vhd) && vhd_batmap_test(vhd, &vhd->batmap, block)) { + if (parent->file) + err = vhd_io_write(parent, buf, sec, vhd->spb); + else + err = __raw_io_write(parent_fd, buf, sec, vhd->spb); + goto done; + } + + err = vhd_read_bitmap(vhd, block, &map); + if (err) + goto done; + + for (i = 0; i < vhd->spb; i++) { + if (!vhd_bitmap_test(vhd, map, i)) + continue; + + for (secs = 0; i + secs < vhd->spb; secs++) + if (!vhd_bitmap_test(vhd, map, i + secs)) + break; + + if (parent->file) + err = vhd_io_write(parent, + buf + vhd_sectors_to_bytes(i), + sec + i, secs); + else + err = __raw_io_write(parent_fd, + buf + vhd_sectors_to_bytes(i), + sec + i, secs); + if (err) + goto done; + + i += secs; + } + + err = 0; + +done: + free(buf); + free(map); + return err; +} + +int +vhd_util_coalesce(int argc, char **argv) +{ + int err, c; + uint64_t i; + char *name, *pname; + vhd_context_t vhd, parent; + int parent_fd = -1; + + name = NULL; + pname = NULL; + parent.file = NULL; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'h': + default: + goto usage; + } + } + + if (!name || optind != argc) + goto usage; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + err = vhd_parent_locator_get(&vhd, &pname); + if (err) { + printf("error finding %s parent: %d\n", name, err); + vhd_close(&vhd); + return err; + } + + if (vhd_parent_raw(&vhd)) { + parent_fd = open(pname, O_RDWR | O_DIRECT | O_LARGEFILE, 0644); + if (parent_fd == -1) { + err = -errno; + printf("failed to open parent %s: %d\n", pname, err); + vhd_close(&vhd); + return err; + } + } else { + err = vhd_open(&parent, pname, VHD_OPEN_RDWR); + if (err) { + printf("error opening %s: %d\n", pname, err); + free(pname); + vhd_close(&vhd); + return err; + } + } + + err = vhd_get_bat(&vhd); + if (err) + goto done; + + if (vhd_has_batmap(&vhd)) { + err = vhd_get_batmap(&vhd); + if (err) + goto done; + } + + for (i = 0; i < vhd.bat.entries; i++) { + err = vhd_util_coalesce_block(&vhd, &parent, parent_fd, i); + if (err) + goto done; + } + + err = 0; + + done: + free(pname); + vhd_close(&vhd); + if (parent.file) + vhd_close(&parent); + else + close(parent_fd); + return err; + +usage: + printf("options: <-n name> [-h help]\n"); + return -EINVAL; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-create.c b/tools/vhd-tools/vhd/lib/vhd-util-create.c new file mode 100644 index 00000000000..a9bdf05feef --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-create.c @@ -0,0 +1,80 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include + +#include "libvhd.h" + +int +vhd_util_create(int argc, char **argv) +{ + char *name; + uint64_t size; + int c, sparse, err; + vhd_flag_creat_t flags; + + err = -EINVAL; + size = 0; + sparse = 1; + name = NULL; + flags = 0; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:s:rh")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 's': + err = 0; + size = strtoull(optarg, NULL, 10); + break; + case 'r': + sparse = 0; + break; + case 'h': + default: + goto usage; + } + } + + if (err || !name || optind != argc) + goto usage; + + return vhd_create(name, size << 20, + (sparse ? HD_TYPE_DYNAMIC : HD_TYPE_FIXED), + flags); + +usage: + printf("options: <-n name> <-s size (MB)> [-r reserve] [-h help]\n"); + return -EINVAL; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-fill.c b/tools/vhd-tools/vhd/lib/vhd-util-fill.c new file mode 100644 index 00000000000..afbfccee48a --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-fill.c @@ -0,0 +1,105 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include + +#include "libvhd.h" + +int +vhd_util_fill(int argc, char **argv) +{ + int err, c; + char *buf, *name; + vhd_context_t vhd; + uint64_t i, sec, secs; + + buf = NULL; + name = NULL; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'h': + default: + goto usage; + } + } + + if (!name || optind != argc) + goto usage; + + err = vhd_open(&vhd, name, VHD_OPEN_RDWR); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + err = vhd_get_bat(&vhd); + if (err) + goto done; + + err = posix_memalign((void **)&buf, 4096, vhd.header.block_size); + if (err) { + err = -err; + goto done; + } + + sec = 0; + secs = vhd.header.block_size >> VHD_SECTOR_SHIFT; + + for (i = 0; i < vhd.header.max_bat_size; i++) { + err = vhd_io_read(&vhd, buf, sec, secs); + if (err) + goto done; + + err = vhd_io_write(&vhd, buf, sec, secs); + if (err) + goto done; + + sec += secs; + } + + err = 0; + + done: + free(buf); + vhd_close(&vhd); + return err; + +usage: + printf("options: <-n name> [-h help]\n"); + return -EINVAL; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-modify.c b/tools/vhd-tools/vhd/lib/vhd-util-modify.c new file mode 100644 index 00000000000..b563d6a6d3f --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-modify.c @@ -0,0 +1,132 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. + * + * Altering operations: + * + * 1. Change the parent pointer to another file. + * 2. Change the size of the file containing the VHD image. This does NOT + * affect the VHD disk capacity, only the physical size of the file containing + * the VHD. Naturally, it is not possible to set the file size to be less than + * the what VHD utilizes. + * The operation doesn't actually change the file size, but it writes the + * footer in the right location such that resizing the file (manually, as a + * separate step) will produce the correct results. If the new file size is + * greater than the current file size, the file must first be expanded and then + * altered with this operation. If the new size is smaller than the current + * size, the VHD must first be altered with this operation and then the file + * must be shrunk. Failing to resize the file will result in a corrupted VHD. + */ + +#include +#include +#include +#include +#include + +#include "libvhd.h" + +TEST_FAIL_EXTERN_VARS; + +int +vhd_util_modify(int argc, char **argv) +{ + char *name; + vhd_context_t vhd; + int err, c, size, parent, parent_raw; + off_t newsize = 0; + char *newparent = NULL; + + name = NULL; + size = 0; + parent = 0; + parent_raw = 0; + + optind = 0; + while ((c = getopt(argc, argv, "n:s:p:mh")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 's': + size = 1; + errno = 0; + newsize = strtoll(optarg, NULL, 10); + if (errno) { + fprintf(stderr, "Invalid size '%s'\n", optarg); + goto usage; + } + break; + case 'p': + parent = 1; + newparent = optarg; + break; + case 'm': + parent_raw = 1; + break; + + case 'h': + default: + goto usage; + } + } + + if (!name || optind != argc) + goto usage; + + err = vhd_open(&vhd, name, VHD_OPEN_RDWR); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + if (size) { + err = vhd_set_phys_size(&vhd, newsize); + if (err) + printf("failed to set physical size to %"PRIu64":" + " %d\n", newsize, err); + } + + if (parent) { + TEST_FAIL_AT(FAIL_REPARENT_BEGIN); + err = vhd_change_parent(&vhd, newparent, parent_raw); + if (err) { + printf("failed to set parent to '%s': %d\n", + newparent, err); + goto done; + } + TEST_FAIL_AT(FAIL_REPARENT_END); + } + +done: + vhd_close(&vhd); + return err; + +usage: + printf("*** Dangerous operations, use with care ***\n"); + printf("options: <-n name> [-p NEW_PARENT set parent [-m raw]] " + "[-s NEW_SIZE set size] [-h help]\n"); + return -EINVAL; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-query.c b/tools/vhd-tools/vhd/lib/vhd-util-query.c new file mode 100644 index 00000000000..44a22d0335d --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-query.c @@ -0,0 +1,159 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include + +#include "libvhd.h" + +int +vhd_util_query(int argc, char **argv) +{ + char *name; + vhd_context_t vhd; + off_t currsize; + int ret, err, c, size, physize, parent, fields, depth; + + name = NULL; + size = 0; + physize = 0; + parent = 0; + fields = 0; + depth = 0; + + if (!argc || !argv) { + err = -EINVAL; + goto usage; + } + + optind = 0; + while ((c = getopt(argc, argv, "n:vspfdh")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'v': + size = 1; + break; + case 's': + physize = 1; + break; + case 'p': + parent = 1; + break; + case 'f': + fields = 1; + break; + case 'd': + depth = 1; + break; + case 'h': + err = 0; + goto usage; + default: + err = -EINVAL; + goto usage; + } + } + + if (!name || optind != argc) { + err = -EINVAL; + goto usage; + } + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + if (size) + printf("%"PRIu64"\n", vhd.footer.curr_size >> 20); + + if (physize) { + err = vhd_get_phys_size(&vhd, &currsize); + if (err) + printf("failed to get physical size: %d\n", err); + else + printf("%"PRIu64"\n", currsize); + } + + if (parent) { + ret = 0; + + if (vhd.footer.type != HD_TYPE_DIFF) + printf("%s has no parent\n", name); + else { + char *pname; + + ret = vhd_parent_locator_get(&vhd, &pname); + if (ret) + printf("query failed\n"); + else { + printf("%s\n", pname); + free(pname); + } + } + + err = (err ? : ret); + } + + if (fields) { + int hidden; + + ret = vhd_hidden(&vhd, &hidden); + if (ret) + printf("error checking 'hidden' field: %d\n", ret); + else + printf("hidden: %d\n", hidden); + + err = (err ? : ret); + } + + if (depth) { + int length; + + ret = vhd_chain_depth(&vhd, &length); + if (ret) + printf("error checking chain depth: %d\n", ret); + else + printf("chain depth: %d\n", length); + + err = (err ? : ret); + } + + vhd_close(&vhd); + return err; + +usage: + printf("options: <-n name> [-v print virtual size (in MB)] " + "[-s print physical utilization (bytes)] [-p print parent] " + "[-f print fields] [-d print chain depth] [-h help]\n"); + return err; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-read.c b/tools/vhd-tools/vhd/lib/vhd-util-read.c new file mode 100644 index 00000000000..2e8dc8b786f --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-read.c @@ -0,0 +1,742 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include +#include + +#include "libvhd.h" +#include "vhd-util.h" + +#define nsize 15 +static char nbuf[nsize]; + +static inline char * +__xconv(uint64_t num) +{ + snprintf(nbuf, nsize, "%#" PRIx64 , num); + return nbuf; +} + +static inline char * +__dconv(uint64_t num) +{ + snprintf(nbuf, nsize, "%" PRIu64, num); + return nbuf; +} + +#define conv(hex, num) \ + (hex ? __xconv((uint64_t)num) : __dconv((uint64_t)num)) + +static void +vhd_print_header(vhd_context_t *vhd, vhd_header_t *h, int hex) +{ + int err; + uint32_t cksm; + char uuid[39], time_str[26], cookie[9], out[512], *name; + + printf("VHD Header Summary:\n-------------------\n"); + + snprintf(cookie, sizeof(cookie), "%s", h->cookie); + printf("Cookie : %s\n", cookie); + + printf("Data offset (unusd) : %s\n", conv(hex, h->data_offset)); + printf("Table offset : %s\n", conv(hex, h->table_offset)); + printf("Header version : 0x%08x\n", h->hdr_ver); + printf("Max BAT size : %s\n", conv(hex, h->max_bat_size)); + printf("Block size : %s ", conv(hex, h->block_size)); + printf("(%s MB)\n", conv(hex, h->block_size >> 20)); + + err = vhd_header_decode_parent(vhd, h, &name); + printf("Parent name : %s\n", + (err ? "failed to read name" : name)); + free(name); + + blk_uuid_to_string(&h->prt_uuid, uuid, sizeof(uuid)); + printf("Parent UUID : %s\n", uuid); + + vhd_time_to_string(h->prt_ts, time_str); + printf("Parent timestamp : %s\n", time_str); + + cksm = vhd_checksum_header(h); + printf("Checksum : 0x%x|0x%x (%s)\n", h->checksum, cksm, + h->checksum == cksm ? "Good!" : "Bad!"); + printf("\n"); +} + +static void +vhd_print_footer(vhd_footer_t *f, int hex) +{ + uint64_t c, h, s; + uint32_t ff_maj, ff_min, cr_maj, cr_min, cksm, cksm_save; + char time_str[26], creator[5], uuid[39], cookie[9]; + + printf("VHD Footer Summary:\n-------------------\n"); + + snprintf(cookie, sizeof(cookie), "%s", f->cookie); + printf("Cookie : %s\n", cookie); + + printf("Features : (0x%08x) %s%s\n", f->features, + (f->features & HD_TEMPORARY) ? "" : "", + (f->features & HD_RESERVED) ? "" : ""); + + ff_maj = f->ff_version >> 16; + ff_min = f->ff_version & 0xffff; + printf("File format version : Major: %d, Minor: %d\n", + ff_maj, ff_min); + + printf("Data offset : %s\n", conv(hex, f->data_offset)); + + vhd_time_to_string(f->timestamp, time_str); + printf("Timestamp : %s\n", time_str); + + memcpy(creator, f->crtr_app, 4); + creator[4] = '\0'; + printf("Creator Application : '%s'\n", creator); + + cr_maj = f->crtr_ver >> 16; + cr_min = f->crtr_ver & 0xffff; + printf("Creator version : Major: %d, Minor: %d\n", + cr_maj, cr_min); + + printf("Creator OS : %s\n", + ((f->crtr_os == HD_CR_OS_WINDOWS) ? "Windows" : + ((f->crtr_os == HD_CR_OS_MACINTOSH) ? "Macintosh" : + "Unknown!"))); + + printf("Original disk size : %s MB ", conv(hex, f->orig_size >> 20)); + printf("(%s Bytes)\n", conv(hex, f->orig_size)); + + printf("Current disk size : %s MB ", conv(hex, f->curr_size >> 20)); + printf("(%s Bytes)\n", conv(hex, f->curr_size)); + + c = f->geometry >> 16; + h = (f->geometry & 0x0000FF00) >> 8; + s = f->geometry & 0x000000FF; + printf("Geometry : Cyl: %s, ", conv(hex, c)); + printf("Hds: %s, ", conv(hex, h)); + printf("Sctrs: %s\n", conv(hex, s)); + printf(" : = %s MB ", conv(hex, (c * h * s) >> 11)); + printf("(%s Bytes)\n", conv(hex, c * h * s << 9)); + + printf("Disk type : %s\n", + f->type <= HD_TYPE_MAX ? + HD_TYPE_STR[f->type] : "Unknown type!\n"); + + cksm = vhd_checksum_footer(f); + printf("Checksum : 0x%x|0x%x (%s)\n", f->checksum, cksm, + f->checksum == cksm ? "Good!" : "Bad!"); + + blk_uuid_to_string(&f->uuid, uuid, sizeof(uuid)); + printf("UUID : %s\n", uuid); + + printf("Saved state : %s\n", f->saved == 0 ? "No" : "Yes"); + printf("Hidden : %d\n", f->hidden); + printf("\n"); +} + +static inline char * +code_name(uint32_t code) +{ + switch(code) { + case PLAT_CODE_NONE: + return "PLAT_CODE_NONE"; + case PLAT_CODE_WI2R: + return "PLAT_CODE_WI2R"; + case PLAT_CODE_WI2K: + return "PLAT_CODE_WI2K"; + case PLAT_CODE_W2RU: + return "PLAT_CODE_W2RU"; + case PLAT_CODE_W2KU: + return "PLAT_CODE_W2KU"; + case PLAT_CODE_MAC: + return "PLAT_CODE_MAC"; + case PLAT_CODE_MACX: + return "PLAT_CODE_MACX"; + default: + return "UNKOWN"; + } +} + +static void +vhd_print_parent(vhd_context_t *vhd, vhd_parent_locator_t *loc) +{ + int err; + char *buf; + + err = vhd_parent_locator_read(vhd, loc, &buf); + if (err) { + printf("failed to read parent name\n"); + return; + } + + printf(" decoded name : %s\n", buf); +} + +static void +vhd_print_parent_locators(vhd_context_t *vhd, int hex) +{ + int i, n; + vhd_parent_locator_t *loc; + + printf("VHD Parent Locators:\n--------------------\n"); + + n = sizeof(vhd->header.loc) / sizeof(struct prt_loc); + for (i = 0; i < n; i++) { + loc = &vhd->header.loc[i]; + + if (loc->code == PLAT_CODE_NONE) + continue; + + printf("locator: : %d\n", i); + printf(" code : %s\n", + code_name(loc->code)); + printf(" data_space : %s\n", + conv(hex, loc->data_space)); + printf(" data_length : %s\n", + conv(hex, loc->data_len)); + printf(" data_offset : %s\n", + conv(hex, loc->data_offset)); + vhd_print_parent(vhd, loc); + printf("\n"); + } +} + +static void +vhd_print_batmap_header(vhd_batmap_t *batmap, int hex) +{ + uint32_t cksm; + + printf("VHD Batmap Summary:\n-------------------\n"); + printf("Batmap offset : %s\n", + conv(hex, batmap->header.batmap_offset)); + printf("Batmap size (secs) : %s\n", + conv(hex, batmap->header.batmap_size)); + printf("Batmap version : 0x%08x\n", + batmap->header.batmap_version); + + cksm = vhd_checksum_batmap(batmap); + printf("Checksum : 0x%x|0x%x (%s)\n", + batmap->header.checksum, cksm, + (batmap->header.checksum == cksm ? "Good!" : "Bad!")); + printf("\n"); +} + +static inline int +check_block_range(vhd_context_t *vhd, uint64_t block, int hex) +{ + if (block > vhd->header.max_bat_size) { + fprintf(stderr, "block %s past end of file\n", + conv(hex, block)); + return -ERANGE; + } + + return 0; +} + +static int +vhd_print_headers(vhd_context_t *vhd, int hex) +{ + int err; + + vhd_print_footer(&vhd->footer, hex); + + if (vhd_type_dynamic(vhd)) { + vhd_print_header(vhd, &vhd->header, hex); + + if (vhd->footer.type == HD_TYPE_DIFF) + vhd_print_parent_locators(vhd, hex); + + if (vhd_has_batmap(vhd)) { + err = vhd_get_batmap(vhd); + if (err) { + printf("failed to get batmap header\n"); + return err; + } + + vhd_print_batmap_header(&vhd->batmap, hex); + } + } + + return 0; +} + +static int +vhd_dump_headers(const char *name, int hex) +{ + vhd_context_t vhd; + + libvhd_set_log_level(1); + memset(&vhd, 0, sizeof(vhd)); + + printf("\n%s appears invalid; dumping headers\n\n", name); + + vhd.fd = open(name, O_DIRECT | O_LARGEFILE | O_RDONLY); + if (vhd.fd == -1) + return -errno; + + vhd.file = strdup(name); + + vhd_read_footer(&vhd, &vhd.footer); + vhd_read_header(&vhd, &vhd.header); + + vhd_print_footer(&vhd.footer, hex); + vhd_print_header(&vhd, &vhd.header, hex); + + close(vhd.fd); + free(vhd.file); + + return 0; +} + +static int +vhd_print_logical_to_physical(vhd_context_t *vhd, + uint64_t sector, int count, int hex) +{ + int i; + uint32_t blk, lsec; + uint64_t cur, offset; + + if (vhd_sectors_to_bytes(sector + count) > vhd->footer.curr_size) { + fprintf(stderr, "sector %s past end of file\n", + conv(hex, sector + count)); + return -ERANGE; + } + + for (i = 0; i < count; i++) { + cur = sector + i; + blk = cur / vhd->spb; + lsec = cur % vhd->spb; + offset = vhd->bat.bat[blk]; + + if (offset != DD_BLK_UNUSED) { + offset += lsec + 1; + offset = vhd_sectors_to_bytes(offset); + } + + printf("logical sector %s: ", conv(hex, cur)); + printf("block number: %s, ", conv(hex, blk)); + printf("sector offset: %s, ", conv(hex, lsec)); + printf("file offset: %s\n", (offset == DD_BLK_UNUSED ? + "not allocated" : conv(hex, offset))); + } + + return 0; +} + +static int +vhd_print_bat(vhd_context_t *vhd, uint64_t block, int count, int hex) +{ + int i; + uint64_t cur, offset; + + if (check_block_range(vhd, block + count, hex)) + return -ERANGE; + + for (i = 0; i < count; i++) { + cur = block + i; + offset = vhd->bat.bat[cur]; + + printf("block: %s: ", conv(hex, cur)); + printf("offset: %s\n", + (offset == DD_BLK_UNUSED ? "not allocated" : + conv(hex, vhd_sectors_to_bytes(offset)))); + } + + return 0; +} + +static inline void +write_full(int fd, void* buf, size_t count) +{ + ssize_t num_written = 0; + if (!buf) return; + + + while(count > 0) { + + num_written = write(fd, buf, count); + if (num_written == -1) { + if (errno == EINTR) + continue; + else + return; + } + + count -= num_written; + buf += num_written; + } +} + +static int +vhd_print_bitmap(vhd_context_t *vhd, uint64_t block, int count, int hex) +{ + char *buf; + int i, err; + uint64_t cur; + + if (check_block_range(vhd, block + count, hex)) + return -ERANGE; + + for (i = 0; i < count; i++) { + cur = block + i; + + if (vhd->bat.bat[cur] == DD_BLK_UNUSED) { + printf("block %s not allocated\n", conv(hex, cur)); + continue; + } + + err = vhd_read_bitmap(vhd, cur, &buf); + if (err) + goto out; + + write_full(STDOUT_FILENO, buf, + vhd_sectors_to_bytes(vhd->bm_secs)); + free(buf); + } + + err = 0; +out: + return err; +} + +static int +vhd_test_bitmap(vhd_context_t *vhd, uint64_t sector, int count, int hex) +{ + char *buf; + uint64_t cur; + int i, err, bit; + uint32_t blk, bm_blk, sec; + + if (vhd_sectors_to_bytes(sector + count) > vhd->footer.curr_size) { + printf("sector %s past end of file\n", conv(hex, sector)); + return -ERANGE; + } + + bm_blk = -1; + buf = NULL; + + for (i = 0; i < count; i++) { + cur = sector + i; + blk = cur / vhd->spb; + sec = cur % vhd->spb; + + if (blk != bm_blk) { + bm_blk = blk; + free(buf); + buf = NULL; + + if (vhd->bat.bat[blk] != DD_BLK_UNUSED) { + err = vhd_read_bitmap(vhd, blk, &buf); + if (err) + goto out; + } + } + + if (vhd->bat.bat[blk] == DD_BLK_UNUSED) + bit = 0; + else + bit = vhd_bitmap_test(vhd, buf, blk); + + print: + printf("block %s: ", conv(hex, blk)); + printf("sec: %s: %d\n", conv(hex, sec), bit); + } + + err = 0; + out: + free(buf); + return err; +} + +static int +vhd_print_batmap(vhd_context_t *vhd) +{ + int err; + size_t size; + + err = vhd_get_batmap(vhd); + if (err) { + printf("failed to read batmap: %d\n", err); + return err; + } + + size = vhd_sectors_to_bytes(vhd->batmap.header.batmap_size); + write_full(STDOUT_FILENO, vhd->batmap.map, size); + + return 0; +} + +static int +vhd_test_batmap(vhd_context_t *vhd, uint64_t block, int count, int hex) +{ + int i, err; + uint64_t cur; + + if (check_block_range(vhd, block + count, hex)) + return -ERANGE; + + err = vhd_get_batmap(vhd); + if (err) { + fprintf(stderr, "failed to get batmap\n"); + return err; + } + + for (i = 0; i < count; i++) { + cur = block + i; + fprintf(stderr, "batmap for block %s: %d\n", conv(hex, cur), + vhd_batmap_test(vhd, &vhd->batmap, cur)); + } + + return 0; +} + +static int +vhd_print_data(vhd_context_t *vhd, uint64_t block, int count, int hex) +{ + char *buf; + int i, err; + uint64_t cur; + + err = 0; + + if (check_block_range(vhd, block + count, hex)) + return -ERANGE; + + for (i = 0; i < count; i++) { + cur = block + i; + + if (vhd->bat.bat[cur] == DD_BLK_UNUSED) { + printf("block %s not allocated\n", conv(hex, cur)); + continue; + } + + err = vhd_read_block(vhd, cur, &buf); + if (err) + break; + + write_full(STDOUT_FILENO, buf, vhd->header.block_size); + free(buf); + } + + return err; +} + +static int +vhd_read_data(vhd_context_t *vhd, uint64_t sec, int count, int hex) +{ + char *buf; + uint64_t cur; + int err, max, secs; + + if (vhd_sectors_to_bytes(sec + count) > vhd->footer.curr_size) + return -ERANGE; + + max = MIN(vhd_sectors_to_bytes(count), VHD_BLOCK_SIZE); + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, max); + if (err) + return -err; + + cur = sec; + while (count) { + secs = MIN((max >> VHD_SECTOR_SHIFT), count); + err = vhd_io_read(vhd, buf, cur, secs); + if (err) + break; + + write_full(STDOUT_FILENO, buf, vhd_sectors_to_bytes(secs)); + + cur += secs; + count -= secs; + } + + free(buf); + return err; +} + +int +vhd_util_read(int argc, char **argv) +{ + char *name; + vhd_context_t vhd; + int c, err, headers, hex; + uint64_t bat, bitmap, tbitmap, batmap, tbatmap, data, lsec, count, read; + + err = 0; + hex = 0; + headers = 0; + count = 1; + bat = -1; + bitmap = -1; + tbitmap = -1; + batmap = -1; + tbatmap = -1; + data = -1; + lsec = -1; + read = -1; + name = NULL; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:pt:b:m:i:aj:d:c:r:xh")) != -1) { + switch(c) { + case 'n': + name = optarg; + break; + case 'p': + headers = 1; + break; + case 't': + lsec = strtoul(optarg, NULL, 10); + break; + case 'b': + bat = strtoull(optarg, NULL, 10); + break; + case 'm': + bitmap = strtoull(optarg, NULL, 10); + break; + case 'i': + tbitmap = strtoul(optarg, NULL, 10); + break; + case 'a': + batmap = 1; + break; + case 'j': + tbatmap = strtoull(optarg, NULL, 10); + break; + case 'd': + data = strtoull(optarg, NULL, 10); + break; + case 'r': + read = strtoull(optarg, NULL, 10); + break; + case 'c': + count = strtoul(optarg, NULL, 10); + break; + case 'x': + hex = 1; + break; + case 'h': + default: + goto usage; + } + } + + if (!name || optind != argc) + goto usage; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED); + if (err) { + printf("Failed to open %s: %d\n", name, err); + vhd_dump_headers(name, hex); + return err; + } + + err = vhd_get_bat(&vhd); + if (err) { + printf("Failed to get bat for %s: %d\n", name, err); + goto out; + } + + if (headers) + vhd_print_headers(&vhd, hex); + + if (lsec != -1) { + err = vhd_print_logical_to_physical(&vhd, lsec, count, hex); + if (err) + goto out; + } + + if (bat != -1) { + err = vhd_print_bat(&vhd, bat, count, hex); + if (err) + goto out; + } + + if (bitmap != -1) { + err = vhd_print_bitmap(&vhd, bitmap, count, hex); + if (err) + goto out; + } + + if (tbitmap != -1) { + err = vhd_test_bitmap(&vhd, tbitmap, count, hex); + if (err) + goto out; + } + + if (batmap != -1) { + err = vhd_print_batmap(&vhd); + if (err) + goto out; + } + + if (tbatmap != -1) { + err = vhd_test_batmap(&vhd, tbatmap, count, hex); + if (err) + goto out; + } + + if (data != -1) { + err = vhd_print_data(&vhd, data, count, hex); + if (err) + goto out; + } + + if (read != -1) { + err = vhd_read_data(&vhd, read, count, hex); + if (err) + goto out; + } + + err = 0; + + out: + vhd_close(&vhd); + return err; + + usage: + printf("options:\n" + "-h help\n" + "-n name\n" + "-p print VHD headers\n" + "-t sec translate logical sector to VHD location\n" + "-b blk print bat entry\n" + "-m blk print bitmap\n" + "-i sec test bitmap for logical sector\n" + "-a print batmap\n" + "-j blk test batmap for block\n" + "-d blk print data\n" + "-c num num units\n" + "-r sec read num sectors at sec\n" + "-x print in hex\n"); + return EINVAL; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-repair.c b/tools/vhd-tools/vhd/lib/vhd-util-repair.c new file mode 100644 index 00000000000..14ded81c29c --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-repair.c @@ -0,0 +1,84 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include + +#include "libvhd.h" + +int +vhd_util_repair(int argc, char **argv) +{ + char *name; + int err, c; + off_t eof; + vhd_context_t vhd; + + name = NULL; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'h': + default: + goto usage; + } + } + + if (!name || optind != argc) + goto usage; + + err = vhd_open(&vhd, name, VHD_OPEN_RDWR); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + err = vhd_end_of_data(&vhd, &eof); + if (err) { + printf("error finding end of data: %d\n", err); + goto done; + } + + err = vhd_write_footer_at(&vhd, &vhd.footer, eof); + + done: + vhd_close(&vhd); + return err; + +usage: + printf("options: <-n name> [-h help]\n"); + return -EINVAL; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-resize.c b/tools/vhd-tools/vhd/lib/vhd-util-resize.c new file mode 100644 index 00000000000..c8a9528530e --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-resize.c @@ -0,0 +1,1131 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libvhd-journal.h" + +#if 1 +#define DFPRINTF(_f, _a...) fprintf(stdout, _f, ##_a) +#else +#define DFPRINTF(_f, _a...) ((void)0) +#endif + +#define EPRINTF(_f, _a...) \ + do { \ + syslog(LOG_INFO, "%s: " _f, __func__, ##_a); \ + DFPRINTF(_f, _a); \ + } while (0) + +typedef struct vhd_block { + uint32_t block; + uint32_t offset; +} vhd_block_t; + +TEST_FAIL_EXTERN_VARS; + +static inline uint32_t +secs_to_blocks_down(vhd_context_t *vhd, uint64_t secs) +{ + return secs / vhd->spb; +} + +static uint32_t +secs_to_blocks_up(vhd_context_t *vhd, uint64_t secs) +{ + uint32_t blocks; + + blocks = secs / vhd->spb; + if (secs % vhd->spb) + blocks++; + + return blocks; +} + +static int +vhd_fixed_shrink(vhd_journal_t *journal, uint64_t secs) +{ + int err; + uint64_t new_eof; + vhd_context_t *vhd; + + vhd = &journal->vhd; + + new_eof = vhd->footer.curr_size - vhd_sectors_to_bytes(secs); + if (new_eof <= sizeof(vhd_footer_t)) + return -EINVAL; + + err = ftruncate(vhd->fd, new_eof); + if (err) + return errno; + + vhd->footer.curr_size = new_eof; + return vhd_write_footer(vhd, &vhd->footer); +} + +static int +vhd_write_zeros(vhd_journal_t *journal, off_t off, uint64_t size) +{ + int err; + char *buf; + vhd_context_t *vhd; + uint64_t bytes, map; + + vhd = &journal->vhd; + map = MIN(size, VHD_BLOCK_SIZE); + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + return err; + + buf = mmap(0, map, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) + return -errno; + + do { + bytes = MIN(size, map); + + err = vhd_write(vhd, buf, bytes); + if (err) + break; + + size -= bytes; + } while (size); + + munmap(buf, map); + + return err; +} + +static int +vhd_fixed_grow(vhd_journal_t *journal, uint64_t secs) +{ + int err; + vhd_context_t *vhd; + uint64_t size, eof, new_eof; + + size = vhd_sectors_to_bytes(secs); + vhd = &journal->vhd; + + err = vhd_seek(vhd, 0, SEEK_END); + if (err) + goto out; + + eof = vhd_position(vhd); + if (eof == (off_t)-1) { + err = -errno; + goto out; + } + + err = vhd_write_zeros(journal, eof - sizeof(vhd_footer_t), size); + if (err) + goto out; + + new_eof = eof + size; + err = vhd_seek(vhd, new_eof, SEEK_SET); + if (err) + goto out; + + vhd->footer.curr_size += size; + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + goto out; + + err = 0; + +out: + return err; +} + +static int +vhd_fixed_resize(vhd_journal_t *journal, uint64_t size) +{ + int err; + vhd_context_t *vhd; + uint64_t cur_secs, new_secs; + + vhd = &journal->vhd; + cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT; + new_secs = size << (20 - VHD_SECTOR_SHIFT); + + if (cur_secs == new_secs) + return 0; + else if (cur_secs > new_secs) + err = vhd_fixed_shrink(journal, cur_secs - new_secs); + else + err = vhd_fixed_grow(journal, new_secs - cur_secs); + + return err; +} + +static inline void +swap(vhd_block_t *list, int a, int b) +{ + vhd_block_t tmp; + + tmp = list[a]; + list[a] = list[b]; + list[b] = tmp; +} + +static int +partition(vhd_block_t *list, int left, int right, int pidx) +{ + int i, sidx; + long long pval; + + sidx = left; + pval = list[pidx].offset; + swap(list, pidx, right); + + for (i = left; i < right; i++) + if (list[i].offset >= pval) { + swap(list, sidx, i); + ++sidx; + } + + swap(list, right, sidx); + return sidx; +} + +static void +quicksort(vhd_block_t *list, int left, int right) +{ + int pidx, new_pidx; + + if (right < left) + return; + + pidx = left; + new_pidx = partition(list, left, right, pidx); + quicksort(list, left, new_pidx - 1); + quicksort(list, new_pidx + 1, right); +} + +static int +vhd_move_block(vhd_journal_t *journal, uint32_t src, off_t offset) +{ + int err; + char *buf; + size_t size; + vhd_context_t *vhd; + off_t off, src_off; + + buf = NULL; + vhd = &journal->vhd; + off = offset; + size = vhd_sectors_to_bytes(vhd->bm_secs); + src_off = vhd->bat.bat[src]; + + if (src_off == DD_BLK_UNUSED) + return -EINVAL; + src_off = vhd_sectors_to_bytes(src_off); + + err = vhd_journal_add_block(journal, src, + VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA); + if (err) + goto out; + + err = vhd_read_bitmap(vhd, src, &buf); + if (err) + goto out; + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + goto out; + + err = vhd_write(vhd, buf, size); + if (err) + goto out; + + free(buf); + buf = NULL; + off += size; + size = vhd_sectors_to_bytes(vhd->spb); + + err = vhd_read_block(vhd, src, &buf); + if (err) + goto out; + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + goto out; + + err = vhd_write(vhd, buf, size); + if (err) + goto out; + + vhd->bat.bat[src] = offset >> VHD_SECTOR_SHIFT; + + err = vhd_write_zeros(journal, src_off, + vhd_sectors_to_bytes(vhd->bm_secs + vhd->spb)); + +out: + free(buf); + return err; +} + +static int +vhd_clobber_block(vhd_journal_t *journal, uint32_t src, uint32_t dest) +{ + int err; + off_t off; + vhd_context_t *vhd; + + vhd = &journal->vhd; + off = vhd_sectors_to_bytes(vhd->bat.bat[dest]); + + err = vhd_journal_add_block(journal, dest, + VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA); + if (err) + return err; + + err = vhd_move_block(journal, src, off); + if (err) + return err; + + vhd->bat.bat[dest] = DD_BLK_UNUSED; + + return 0; +} + +/* + * remove a list of blocks from the vhd file + * if a block to be removed: + * - resides at the end of the file: simply clear its bat entry + * - resides elsewhere: move the last block in the file into its position + * and update the bat to reflect this + */ +static int +vhd_defrag_shrink(vhd_journal_t *journal, + vhd_block_t *original_free_list, int free_cnt) +{ + vhd_context_t *vhd; + int i, j, free_idx, err; + vhd_block_t *blocks, *free_list; + + err = 0; + blocks = NULL; + free_list = NULL; + vhd = &journal->vhd; + + blocks = malloc(vhd->bat.entries * sizeof(vhd_block_t)); + if (!blocks) { + err = -ENOMEM; + goto out; + } + + free_list = malloc(free_cnt * sizeof(vhd_block_t)); + if (!free_list) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < vhd->bat.entries; i++) { + blocks[i].block = i; + blocks[i].offset = vhd->bat.bat[i]; + } + + memcpy(free_list, original_free_list, + free_cnt * sizeof(vhd_block_t)); + + /* sort both the to-free list and the bat list + * in order of descending file offset */ + quicksort(free_list, 0, free_cnt - 1); + quicksort(blocks, 0, vhd->bat.entries - 1); + + for (i = 0, free_idx = 0; + i < vhd->bat.entries && free_idx < free_cnt; i++) { + vhd_block_t *b = blocks + i; + + if (b->offset == DD_BLK_UNUSED) + continue; + + for (j = free_idx; j < free_cnt; j++) + if (b->block == free_list[j].block) { + /* the last block in the file is in the list of + * blocks to remove; no need to shuffle the + * data -- just clear the bat entry */ + vhd->bat.bat[free_list[j].block] = DD_BLK_UNUSED; + free_idx++; + continue; + } + + err = vhd_clobber_block(journal, b->block, + free_list[free_idx++].block); + if (err) + goto out; + } + + /* clear any bat entries for blocks we did not shuffle */ + for (i = free_idx; i < free_cnt; i++) + vhd->bat.bat[free_list[i].block] = DD_BLK_UNUSED; + +out: + free(blocks); + free(free_list); + + return err; +} + +static int +vhd_clear_bat_entries(vhd_journal_t *journal, uint32_t entries) +{ + int i, err; + vhd_context_t *vhd; + off_t orig_map_off, new_map_off; + uint32_t orig_entries, new_entries; + + vhd = &journal->vhd; + orig_entries = vhd->header.max_bat_size; + new_entries = orig_entries - entries; + + if (vhd_has_batmap(vhd)) { + err = vhd_batmap_header_offset(vhd, &orig_map_off); + if (err) + return err; + } + + /* update header */ + vhd->header.max_bat_size = new_entries; + err = vhd_write_header(vhd, &vhd->header); + if (err) + return err; + + /* update footer */ + vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size; + vhd->footer.geometry = vhd_chs(vhd->footer.curr_size); + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + return err; + + /* update bat -- we don't reclaim space, just clear entries */ + for (i = new_entries; i < orig_entries; i++) + vhd->bat.bat[i] = 0; + + err = vhd_write_bat(vhd, &vhd->bat); + if (err) + return err; + + /* update this after write_bat so the end of the bat is zeored */ + vhd->bat.entries = new_entries; + + if (!vhd_has_batmap(vhd)) + return 0; + + /* zero out old batmap header if new header has moved */ + err = vhd_batmap_header_offset(vhd, &new_map_off); + if (err) + return err; + + if (orig_map_off != new_map_off) { + size_t size; + + size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr)); + + err = vhd_write_zeros(journal, orig_map_off, size); + if (err) + return err; + } + + /* update batmap -- clear entries for freed blocks */ + for (i = new_entries; i < orig_entries; i++) + vhd_batmap_clear(vhd, &vhd->batmap, i); + + err = vhd_write_batmap(vhd, &vhd->batmap); + if (err) + return err; + + return 0; +} + +static int +vhd_dynamic_shrink(vhd_journal_t *journal, uint64_t secs) +{ + off_t eof; + uint32_t blocks; + vhd_context_t *vhd; + int i, j, err, free_cnt; + struct vhd_block *free_list; + + printf("dynamic shrink not fully implemented\n"); + return -ENOSYS; + + eof = 0; + free_cnt = 0; + free_list = NULL; + vhd = &journal->vhd; + + blocks = secs_to_blocks_down(vhd, secs); + if (blocks == 0) + return 0; + + if (vhd_has_batmap(vhd)) { + err = vhd_get_batmap(vhd); + if (err) + return err; + } + + free_list = malloc(blocks * sizeof(struct vhd_block)); + if (!free_list) + return -ENOMEM; + + for (i = vhd->bat.entries - 1, j = 0; i >= 0 && j < blocks; i--, j++) { + uint32_t blk = vhd->bat.bat[i]; + + if (blk != DD_BLK_UNUSED) { + free_list[free_cnt].block = i; + free_list[free_cnt].offset = blk; + free_cnt++; + } + } + + if (free_cnt) { + err = vhd_defrag_shrink(journal, free_list, free_cnt); + if (err) + goto out; + } + + err = vhd_clear_bat_entries(journal, blocks); + if (err) + goto out; + + /* remove data beyond footer */ + err = vhd_end_of_data(vhd, &eof); + if (err) + goto out; + + err = ftruncate(vhd->fd, eof + sizeof(vhd_footer_t)); + if (err) { + err = -errno; + goto out; + } + + err = 0; + +out: + free(free_list); + return err; +} + +static inline void +vhd_first_data_block(vhd_context_t *vhd, vhd_block_t *block) +{ + int i; + uint32_t blk; + + memset(block, 0, sizeof(vhd_block_t)); + + for (i = 0; i < vhd->bat.entries; i++) { + blk = vhd->bat.bat[i]; + + if (blk != DD_BLK_UNUSED) { + if (!block->offset || blk < block->offset) { + block->block = i; + block->offset = blk; + } + } + } +} + +static inline uint32_t +vhd_next_block_offset(vhd_context_t *vhd) +{ + int i; + uint32_t blk, end, spp, next; + + next = 0; + spp = getpagesize() >> VHD_SECTOR_SHIFT; + + for (i = 0; i < vhd->bat.entries; i++) { + blk = vhd->bat.bat[i]; + + if (blk != DD_BLK_UNUSED) { + end = blk + vhd->spb + vhd->bm_secs; + next = MAX(next, end); + } + } + + return next; +} + +static inline int +in_range(off_t off, off_t start, off_t size) +{ + return (start < off && start + size > off); +} + +#define SKIP_HEADER 0x01 +#define SKIP_BAT 0x02 +#define SKIP_BATMAP 0x04 +#define SKIP_PLOC 0x08 +#define SKIP_DATA 0x10 + +static inline int +skip_check(int mode, int type) +{ + return mode & type; +} + +static int +vhd_check_for_clobber(vhd_context_t *vhd, off_t off, int mode) +{ + int i, n; + char *msg; + size_t size; + vhd_block_t fb; + vhd_parent_locator_t *loc; + + msg = NULL; + + if (!vhd_type_dynamic(vhd)) + return 0; + + if (off < VHD_SECTOR_SIZE) { + msg = "backup footer"; + goto fail; + } + + if (!skip_check(mode, SKIP_HEADER)) + if (in_range(off, + vhd->footer.data_offset, sizeof(vhd_header_t))) { + msg = "header"; + goto fail; + } + + if (!skip_check(mode, SKIP_BAT)) + if (in_range(off, vhd->header.table_offset, + vhd_bytes_padded(vhd->header.max_bat_size * + sizeof(uint32_t)))) { + msg = "bat"; + goto fail; + } + + if (!skip_check(mode, SKIP_BATMAP)) + if (vhd_has_batmap(vhd) && + in_range(off, vhd->batmap.header.batmap_offset, + vhd_bytes_padded(vhd->batmap.header.batmap_size))) { + msg = "batmap"; + goto fail; + } + + if (!skip_check(mode, SKIP_PLOC)) { + n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t); + for (i = 0; i < n; i++) { + loc = vhd->header.loc + i; + if (loc->code == PLAT_CODE_NONE) + continue; + + size = vhd_parent_locator_size(loc); + if (in_range(off, loc->data_offset, size)) { + msg = "parent locator"; + goto fail; + } + } + } + + if (!skip_check(mode, SKIP_DATA)) { + vhd_first_data_block(vhd, &fb); + if (fb.offset && in_range(off, + vhd_sectors_to_bytes(fb.offset), + VHD_BLOCK_SIZE)) { + msg = "data block"; + goto fail; + } + } + + return 0; + +fail: + EPRINTF("write to 0x%08"PRIx64" would clobber %s\n", off, msg); + return -EINVAL; +} + +/* + * take any metadata after the bat (@eob) and shift it + */ +static int +vhd_shift_metadata(vhd_journal_t *journal, off_t eob, + size_t bat_needed, size_t map_needed) +{ + int i, n, err; + vhd_context_t *vhd; + size_t size_needed; + char *buf, **locators; + vhd_parent_locator_t *loc; + + vhd = &journal->vhd; + size_needed = bat_needed + map_needed; + + n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t); + + locators = calloc(n, sizeof(char *)); + if (!locators) + return -ENOMEM; + + for (i = 0; i < n; i++) { + size_t size; + + loc = vhd->header.loc + i; + if (loc->code == PLAT_CODE_NONE) + continue; + + if (loc->data_offset < eob) + continue; + + size = vhd_parent_locator_size(loc); + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + err = -err; + buf = NULL; + goto out; + } + + err = vhd_seek(vhd, loc->data_offset, SEEK_SET); + if (err) + goto out; + + err = vhd_read(vhd, buf, size); + if (err) + goto out; + + locators[i] = buf; + } + + for (i = 0; i < n; i++) { + off_t off; + size_t size; + + if (!locators[i]) + continue; + + loc = vhd->header.loc + i; + off = loc->data_offset + size_needed; + size = vhd_parent_locator_size(loc); + + if (vhd_check_for_clobber(vhd, off + size, SKIP_PLOC)) { + EPRINTF("%s: shifting locator %d would clobber data\n", + vhd->file, i); + return -EINVAL; + } + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + goto out; + + err = vhd_write(vhd, locators[i], size); + if (err) + goto out; + + free(locators[i]); + locators[i] = NULL; + loc->data_offset = off; + + /* write the new header after writing the new bat */ + } + + if (vhd_has_batmap(vhd) && vhd->batmap.header.batmap_offset > eob) { + vhd->batmap.header.batmap_offset += bat_needed; + + /* write the new batmap after writing the new bat */ + } + + err = 0; + +out: + for (i = 0; i < n; i++) + free(locators[i]); + free(locators); + + return err; +} + +static int +vhd_add_bat_entries(vhd_journal_t *journal, int entries) +{ + int i, err; + off_t off; + vhd_bat_t new_bat; + vhd_context_t *vhd; + uint32_t new_entries; + vhd_batmap_t new_batmap; + uint64_t bat_size, new_bat_size, map_size, new_map_size; + + vhd = &journal->vhd; + new_entries = vhd->header.max_bat_size + entries; + + bat_size = vhd_bytes_padded(vhd->header.max_bat_size * + sizeof(uint32_t)); + new_bat_size = vhd_bytes_padded(new_entries * sizeof(uint32_t)); + + map_size = vhd_bytes_padded((vhd->header.max_bat_size + 7) >> 3); + new_map_size = vhd_bytes_padded((new_entries + 7) >> 3); + + off = vhd->header.table_offset + new_bat_size; + if (vhd_check_for_clobber(vhd, off, SKIP_BAT | SKIP_BATMAP)) { + EPRINTF("%s: writing new bat of 0x%"PRIx64" bytes " + "at 0x%08"PRIx64" would clobber data\n", + vhd->file, new_bat_size, vhd->header.table_offset); + return -EINVAL; + } + + if (vhd_has_batmap(vhd)) { + off = vhd->batmap.header.batmap_offset + new_map_size; + if (vhd_check_for_clobber(vhd, off, 0)) { + EPRINTF("%s: writing new batmap of 0x%"PRIx64" bytes" + " at 0x%08"PRIx64" would clobber data\n", vhd->file, + new_map_size, vhd->batmap.header.batmap_offset); + return -EINVAL; + } + } + + /* update header */ + vhd->header.max_bat_size = new_entries; + err = vhd_write_header(vhd, &vhd->header); + if (err) + return err; + + /* update footer */ + vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size; + vhd->footer.geometry = vhd_chs(vhd->footer.curr_size); + vhd->footer.checksum = vhd_checksum_footer(&vhd->footer); + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + return err; + + /* allocate new bat */ + err = posix_memalign((void **)&new_bat.bat, VHD_SECTOR_SIZE, new_bat_size); + if (err) + return -err; + + new_bat.spb = vhd->bat.spb; + new_bat.entries = new_entries; + memcpy(new_bat.bat, vhd->bat.bat, bat_size); + for (i = vhd->bat.entries; i < new_entries; i++) + new_bat.bat[i] = DD_BLK_UNUSED; + + /* write new bat */ + err = vhd_write_bat(vhd, &new_bat); + if (err) { + free(new_bat.bat); + return err; + } + + /* update in-memory bat */ + free(vhd->bat.bat); + vhd->bat = new_bat; + + if (!vhd_has_batmap(vhd)) + return 0; + + /* allocate new batmap */ + err = posix_memalign((void **)&new_batmap.map, + VHD_SECTOR_SIZE, new_map_size); + if (err) + return err; + + new_batmap.header = vhd->batmap.header; + new_batmap.header.batmap_size = secs_round_up_no_zero(new_map_size); + memcpy(new_batmap.map, vhd->batmap.map, map_size); + memset(new_batmap.map + map_size, 0, new_map_size - map_size); + + /* write new batmap */ + err = vhd_write_batmap(vhd, &new_batmap); + if (err) { + free(new_batmap.map); + return err; + } + + /* update in-memory batmap */ + free(vhd->batmap.map); + vhd->batmap = new_batmap; + + return 0; +} + +static int +vhd_dynamic_grow(vhd_journal_t *journal, uint64_t secs) +{ + int i, err; + off_t eob, eom; + vhd_context_t *vhd; + vhd_block_t first_block; + uint64_t blocks, size_needed; + uint64_t bat_needed, bat_size, bat_avail, bat_bytes, bat_secs; + uint64_t map_needed, map_size, map_avail, map_bytes, map_secs; + + vhd = &journal->vhd; + + size_needed = 0; + bat_needed = 0; + map_needed = 0; + + /* number of vhd blocks to add */ + blocks = secs_to_blocks_up(vhd, secs); + + /* size in bytes needed for new bat entries */ + bat_needed = blocks * sizeof(uint32_t); + map_needed = (blocks >> 3) + 1; + + /* available bytes in current bat */ + bat_bytes = vhd->header.max_bat_size * sizeof(uint32_t); + bat_secs = secs_round_up_no_zero(bat_bytes); + bat_size = vhd_sectors_to_bytes(bat_secs); + bat_avail = bat_size - bat_bytes; + + if (vhd_has_batmap(vhd)) { + /* avaliable bytes in current batmap */ + map_bytes = (vhd->header.max_bat_size + 7) >> 3; + map_secs = vhd->batmap.header.batmap_size; + map_size = vhd_sectors_to_bytes(map_secs); + map_avail = map_size - map_bytes; + } else { + map_needed = 0; + map_avail = 0; + } + + /* we have enough space already; just extend the bat */ + if (bat_needed <= bat_avail && map_needed <= map_avail) + goto add_entries; + + /* we need to add new sectors to the bat */ + if (bat_needed > bat_avail) { + bat_needed -= bat_avail; + bat_needed = vhd_bytes_padded(bat_needed); + } else + bat_needed = 0; + + /* we need to add new sectors to the batmap */ + if (map_needed > map_avail) { + map_needed -= map_avail; + map_needed = vhd_bytes_padded(map_needed); + } else + map_needed = 0; + + /* how many additional bytes do we need? */ + size_needed = bat_needed + map_needed; + + /* calculate space between end of headers and beginning of data */ + err = vhd_end_of_headers(vhd, &eom); + if (err) + return err; + + eob = vhd->header.table_offset + vhd_sectors_to_bytes(bat_secs); + vhd_first_data_block(vhd, &first_block); + + /* no blocks allocated; just shift post-bat metadata */ + if (!first_block.offset) + goto shift_metadata; + + /* + * not enough space -- + * move vhd data blocks to the end of the file to make room + */ + do { + off_t new_off, bm_size, gap_size; + + new_off = vhd_sectors_to_bytes(vhd_next_block_offset(vhd)); + + /* data region of segment should begin on page boundary */ + bm_size = vhd_sectors_to_bytes(vhd->bm_secs); + if ((new_off + bm_size) % 4096) { + gap_size = 4096 - ((new_off + bm_size) % 4096); + + err = vhd_write_zeros(journal, new_off, gap_size); + if (err) + return err; + + new_off += gap_size; + } + + err = vhd_move_block(journal, first_block.block, new_off); + if (err) + return err; + + vhd_first_data_block(vhd, &first_block); + + } while (eom + size_needed >= vhd_sectors_to_bytes(first_block.offset)); + + TEST_FAIL_AT(FAIL_RESIZE_DATA_MOVED); + +shift_metadata: + /* shift any metadata after the bat to make room for new bat sectors */ + err = vhd_shift_metadata(journal, eob, bat_needed, map_needed); + if (err) + return err; + + TEST_FAIL_AT(FAIL_RESIZE_METADATA_MOVED); + +add_entries: + return vhd_add_bat_entries(journal, blocks); +} + +static int +vhd_dynamic_resize(vhd_journal_t *journal, uint64_t size) +{ + int err; + vhd_context_t *vhd; + uint64_t cur_secs, new_secs; + + vhd = &journal->vhd; + cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT; + new_secs = size << (20 - VHD_SECTOR_SHIFT); + + if (cur_secs == new_secs) + return 0; + + err = vhd_get_header(vhd); + if (err) + return err; + + err = vhd_get_bat(vhd); + if (err) + return err; + + if (vhd_has_batmap(vhd)) { + err = vhd_get_batmap(vhd); + if (err) + return err; + } + + if (cur_secs > new_secs) + err = vhd_dynamic_shrink(journal, cur_secs - new_secs); + else + err = vhd_dynamic_grow(journal, new_secs - cur_secs); + + return err; +} + +static int +vhd_util_resize_check_creator(const char *name) +{ + int err; + vhd_context_t vhd; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_STRICT); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + if (!vhd_creator_tapdisk(&vhd)) { + printf("%s not created by xen; resize not supported\n", name); + err = -EINVAL; + } + + vhd_close(&vhd); + return err; +} + +int +vhd_util_resize(int argc, char **argv) +{ + char *name, *jname; + uint64_t size; + int c, err, jerr; + vhd_journal_t journal; + vhd_context_t *vhd; + + err = -EINVAL; + size = 0; + name = NULL; + jname = NULL; + + optind = 0; + while ((c = getopt(argc, argv, "n:j:s:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'j': + jname = optarg; + break; + case 's': + err = 0; + size = strtoull(optarg, NULL, 10); + break; + case 'h': + default: + goto usage; + } + } + + if (err || !name || !jname || argc != optind) + goto usage; + + err = vhd_util_resize_check_creator(name); + if (err) + return err; + + libvhd_set_log_level(1); + err = vhd_journal_create(&journal, name, jname); + if (err) { + printf("creating journal failed: %d\n", err); + return err; + } + + vhd = &journal.vhd; + + err = vhd_get_footer(vhd); + if (err) + goto out; + + TEST_FAIL_AT(FAIL_RESIZE_BEGIN); + + if (vhd_type_dynamic(vhd)) + err = vhd_dynamic_resize(&journal, size); + else + err = vhd_fixed_resize(&journal, size); + + TEST_FAIL_AT(FAIL_RESIZE_END); + +out: + if (err) { + printf("resize failed: %d\n", err); + jerr = vhd_journal_revert(&journal); + } else + jerr = vhd_journal_commit(&journal); + + if (jerr) { + printf("closing journal failed: %d\n", jerr); + vhd_journal_close(&journal); + } else + vhd_journal_remove(&journal); + + return (err ? : jerr); + +usage: + printf("options: <-n name> <-j journal> <-s size (in MB)> [-h help]\n"); + return -EINVAL; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-revert.c b/tools/vhd-tools/vhd/lib/vhd-util-revert.c new file mode 100644 index 00000000000..dab6e8b950b --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-revert.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. + * + * Altering operations: + * + * 1. Change the parent pointer to another file. + * 2. Change the size of the file containing the VHD image. This does NOT + * affect the VHD disk capacity, only the physical size of the file containing + * the VHD. Naturally, it is not possible to set the file size to be less than + * the what VHD utilizes. + * The operation doesn't actually change the file size, but it writes the + * footer in the right location such that resizing the file (manually, as a + * separate step) will produce the correct results. If the new file size is + * greater than the current file size, the file must first be expanded and then + * altered with this operation. If the new size is smaller than the current + * size, the VHD must first be altered with this operation and then the file + * must be shrunk. Failing to resize the file will result in a corrupted VHD. +*/ + +#include +//#include +#include +//#include +#include + +#include "libvhd.h" +#include "libvhd-journal.h" + +int +vhd_util_revert(int argc, char **argv) +{ + char *name, *jname; + vhd_journal_t journal; + int c, err; + + name = NULL; + jname = NULL; + + optind = 0; + while ((c = getopt(argc, argv, "n:j:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'j': + jname = optarg; + break; + case 'h': + default: + goto usage; + } + } + + if (!name || !jname || argc != optind) + goto usage; + + libvhd_set_log_level(1); + err = vhd_journal_open(&journal, name, jname); + if (err) { + printf("opening journal failed: %d\n", err); + return err; + } + + err = vhd_journal_revert(&journal); + if (err) { + printf("reverting journal failed: %d\n", err); + vhd_journal_close(&journal); + return err; + } + + err = vhd_journal_remove(&journal); + if (err) { + printf("removing journal failed: %d\n", err); + vhd_journal_close(&journal); + return err; + } + + return 0; + +usage: + printf("options: <-n name> <-j journal> [-h help]\n"); + return -EINVAL; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-scan.c b/tools/vhd-tools/vhd/lib/vhd-util-scan.c new file mode 100644 index 00000000000..e87a7eae000 --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-scan.c @@ -0,0 +1,1317 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include +#include +#include +#include +#include /* for basename() */ +#include + +#include "list.h" +#include "libvhd.h" +#include "lvm-util.h" + +#define VHD_SCAN_FAST 0x01 +#define VHD_SCAN_PRETTY 0x02 +#define VHD_SCAN_VOLUME 0x04 +#define VHD_SCAN_NOFAIL 0x08 +#define VHD_SCAN_VERBOSE 0x10 +#define VHD_SCAN_PARENTS 0x20 + +#define VHD_TYPE_RAW_FILE 0x01 +#define VHD_TYPE_VHD_FILE 0x02 +#define VHD_TYPE_RAW_VOLUME 0x04 +#define VHD_TYPE_VHD_VOLUME 0x08 + +static inline int +target_volume(uint8_t type) +{ + return (type == VHD_TYPE_RAW_VOLUME || type == VHD_TYPE_VHD_VOLUME); +} + +static inline int +target_vhd(uint8_t type) +{ + return (type == VHD_TYPE_VHD_FILE || type == VHD_TYPE_VHD_VOLUME); +} + +struct target { + char name[VHD_MAX_NAME_LEN]; + char device[VHD_MAX_NAME_LEN]; + uint64_t size; + uint64_t start; + uint64_t end; + uint8_t type; +}; + +struct iterator { + int cur; + int cur_size; + int max_size; + struct target *targets; +}; + +struct vhd_image { + char *name; + char *parent; + uint64_t capacity; + off_t size; + uint8_t hidden; + int error; + char *message; + + struct target *target; + + struct list_head sibling; + struct list_head children; + struct vhd_image *parent_image; +}; + +struct vhd_scan { + int cur; + int size; + + int lists_cur; + int lists_size; + + struct vhd_image **images; + struct vhd_image **lists; +}; + +static int flags; +static struct vg vg; +static struct vhd_scan scan; + +static int +vhd_util_scan_pretty_allocate_list(int cnt) +{ + int i; + struct vhd_image *list; + + memset(&scan, 0, sizeof(scan)); + + scan.lists_cur = 1; + scan.lists_size = 10; + + scan.lists = calloc(scan.lists_size, sizeof(struct vhd_image *)); + if (!scan.lists) + goto fail; + + scan.lists[0] = calloc(cnt, sizeof(struct vhd_image)); + if (!scan.lists[0]) + goto fail; + + scan.images = calloc(cnt, sizeof(struct vhd_image *)); + if (!scan.images) + goto fail; + + for (i = 0; i < cnt; i++) + scan.images[i] = scan.lists[0] + i; + + scan.cur = 0; + scan.size = cnt; + + return 0; + +fail: + if (scan.lists) { + free(scan.lists[0]); + free(scan.lists); + } + + free(scan.images); + memset(&scan, 0, sizeof(scan)); + return -ENOMEM; +} + +static void +vhd_util_scan_pretty_free_list(void) +{ + int i; + + if (scan.lists) { + for (i = 0; i < scan.lists_cur; i++) + free(scan.lists[i]); + free(scan.lists); + } + + free(scan.images); + memset(&scan, 0, sizeof(scan)); +} + +static int +vhd_util_scan_pretty_add_image(struct vhd_image *image) +{ + int i; + struct vhd_image *img; + + for (i = 0; i < scan.cur; i++) { + img = scan.images[i]; + if (!strcmp(img->name, image->name)) + return 0; + } + + if (scan.cur >= scan.size) { + struct vhd_image *new, **list; + + if (scan.lists_cur >= scan.lists_size) { + list = realloc(scan.lists, scan.lists_size * 2 * + sizeof(struct vhd_image *)); + if (!list) + return -ENOMEM; + + scan.lists_size *= 2; + scan.lists = list; + } + + new = calloc(scan.size, sizeof(struct vhd_image)); + if (!new) + return -ENOMEM; + + scan.lists[scan.lists_cur++] = new; + scan.size *= 2; + + list = realloc(scan.images, scan.size * + sizeof(struct vhd_image *)); + if (!list) + return -ENOMEM; + + scan.images = list; + for (i = 0; i + scan.cur < scan.size; i++) + scan.images[i + scan.cur] = new + i; + } + + img = scan.images[scan.cur]; + INIT_LIST_HEAD(&img->sibling); + INIT_LIST_HEAD(&img->children); + + img->capacity = image->capacity; + img->size = image->size; + img->hidden = image->hidden; + img->error = image->error; + img->message = image->message; + + img->name = strdup(image->name); + if (!img->name) + goto fail; + + if (image->parent) { + img->parent = strdup(image->parent); + if (!img->parent) + goto fail; + } + + scan.cur++; + return 0; + +fail: + free(img->name); + free(img->parent); + memset(img, 0, sizeof(*img)); + return -ENOMEM; +} + +static int +vhd_util_scan_pretty_image_compare(const void *lhs, const void *rhs) +{ + struct vhd_image *l, *r; + + l = *(struct vhd_image **)lhs; + r = *(struct vhd_image **)rhs; + + return strcmp(l->name, r->name); +} + +static void +vhd_util_scan_print_image_indent(struct vhd_image *image, int tab) +{ + char *pad, *name, *pmsg, *parent; + + pad = (tab ? " " : ""); + name = image->name; + parent = (image->parent ? : "none"); + + if ((flags & VHD_SCAN_PRETTY) && image->parent && !image->parent_image) + pmsg = " (not found in scan)"; + else + pmsg = ""; + + if (!(flags & VHD_SCAN_VERBOSE)) { + name = basename(image->name); + if (image->parent) + parent = basename(image->parent); + } + + if (image->error) + printf("%*svhd=%s scan-error=%d error-message='%s'\n", + tab, pad, image->name, image->error, image->message); + else + printf("%*svhd=%s capacity=%"PRIu64" size=%"PRIu64" hidden=%u " + "parent=%s%s\n", tab, pad, name, image->capacity, + image->size, image->hidden, parent, pmsg); +} + +static void +vhd_util_scan_pretty_print_tree(struct vhd_image *image, int depth) +{ + struct vhd_image *img, *tmp; + + vhd_util_scan_print_image_indent(image, depth * 3); + + list_for_each_entry_safe(img, tmp, &image->children, sibling) + if (!img->hidden) + vhd_util_scan_pretty_print_tree(img, depth + 1); + + list_for_each_entry_safe(img, tmp, &image->children, sibling) + if (img->hidden) + vhd_util_scan_pretty_print_tree(img, depth + 1); + + free(image->name); + free(image->parent); + + image->name = NULL; + image->parent = NULL; +} + +static void +vhd_util_scan_pretty_print_images(void) +{ + int i; + struct vhd_image *image, **parentp, *parent, *keyp, key; + + qsort(scan.images, scan.cur, sizeof(scan.images[0]), + vhd_util_scan_pretty_image_compare); + + for (i = 0; i < scan.cur; i++) { + image = scan.images[i]; + + if (!image->parent) { + image->parent_image = NULL; + continue; + } + + memset(&key, 0, sizeof(key)); + key.name = image->parent; + keyp = &key; + + parentp = bsearch(&keyp, scan.images, scan.cur, + sizeof(scan.images[0]), + vhd_util_scan_pretty_image_compare); + if (!parentp) { + image->parent_image = NULL; + continue; + } + + parent = *parentp; + image->parent_image = parent; + list_add_tail(&image->sibling, &parent->children); + } + + for (i = 0; i < scan.cur; i++) { + image = scan.images[i]; + + if (image->parent_image || !image->hidden) + continue; + + vhd_util_scan_pretty_print_tree(image, 0); + } + + for (i = 0; i < scan.cur; i++) { + image = scan.images[i]; + + if (!image->name || image->parent_image) + continue; + + vhd_util_scan_pretty_print_tree(image, 0); + } + + for (i = 0; i < scan.cur; i++) { + image = scan.images[i]; + + if (!image->name) + continue; + + vhd_util_scan_pretty_print_tree(image, 0); + } +} + +static void +vhd_util_scan_print_image(struct vhd_image *image) +{ + int err; + + if (!image->error && (flags & VHD_SCAN_PRETTY)) { + err = vhd_util_scan_pretty_add_image(image); + if (!err) + return; + + if (!image->error) { + image->error = err; + image->message = "allocating memory"; + } + } + + vhd_util_scan_print_image_indent(image, 0); +} + +static int +vhd_util_scan_error(const char *file, int err) +{ + struct vhd_image image; + + memset(&image, 0, sizeof(image)); + image.name = (char *)file; + image.error = err; + image.message = "failure scanning target"; + + vhd_util_scan_print_image(&image); + + /* + if (flags & VHD_SCAN_NOFAIL) + return 0; + */ + + return err; +} + +static vhd_parent_locator_t * +vhd_util_scan_get_parent_locator(vhd_context_t *vhd) +{ + int i; + vhd_parent_locator_t *loc; + + loc = NULL; + + for (i = 0; i < 8; i++) { + if (vhd->header.loc[i].code == PLAT_CODE_MACX) { + loc = vhd->header.loc + i; + break; + } + + if (vhd->header.loc[i].code == PLAT_CODE_W2RU) + loc = vhd->header.loc + i; + + if (!loc && vhd->header.loc[i].code != PLAT_CODE_NONE) + loc = vhd->header.loc + i; + } + + return loc; +} + +static inline int +copy_name(char *dst, const char *src) +{ + if (snprintf(dst, VHD_MAX_NAME_LEN, "%s", src) < VHD_MAX_NAME_LEN) + return 0; + + return -ENAMETOOLONG; +} + +/* + * LVHD stores realpath(parent) in parent locators, so + * /dev// becomes /dev/mapper/- + */ +static int +vhd_util_scan_extract_volume_name(char *dst, const char *src) +{ + int err; + char copy[VHD_MAX_NAME_LEN], *name, *s, *c; + + name = strrchr(src, '/'); + if (!name) + name = (char *)src; + + /* convert single dashes to slashes, double dashes to single dashes */ + for (c = copy, s = name; *s != '\0'; s++, c++) { + if (*s == '-') { + if (s[1] != '-') + *c = '/'; + else { + s++; + *c = '-'; + } + } else + *c = *s; + } + + *c = '\0'; + c = strrchr(copy, '/'); + if (c == name) { + /* unrecognized format */ + strcpy(dst, src); + return -EINVAL; + } + + strcpy(dst, ++c); + return 0; +} + +static int +vhd_util_scan_get_volume_parent(vhd_context_t *vhd, struct vhd_image *image) +{ + int err; + char name[VHD_MAX_NAME_LEN]; + vhd_parent_locator_t *loc, copy; + + if (flags & VHD_SCAN_FAST) { + err = vhd_header_decode_parent(vhd, + &vhd->header, &image->parent); + if (!err) + goto found; + } + + loc = vhd_util_scan_get_parent_locator(vhd); + if (!loc) + return -EINVAL; + + copy = *loc; + copy.data_offset += image->target->start; + err = vhd_parent_locator_read(vhd, ©, &image->parent); + if (err) + return err; + +found: + err = vhd_util_scan_extract_volume_name(name, image->parent); + if (!err) + return copy_name(image->parent, name); + + return 0; +} + +static int +vhd_util_scan_get_parent(vhd_context_t *vhd, struct vhd_image *image) +{ + int i, err; + vhd_parent_locator_t *loc; + + if (!target_vhd(image->target->type)) { + image->parent = NULL; + return 0; + } + + loc = NULL; + + if (target_volume(image->target->type)) + return vhd_util_scan_get_volume_parent(vhd, image); + + if (flags & VHD_SCAN_FAST) { + err = vhd_header_decode_parent(vhd, + &vhd->header, &image->parent); + if (!err) + return 0; + } else { + /* + * vhd_parent_locator_get checks for the existence of the + * parent file. if this call succeeds, all is well; if not, + * we'll try to return whatever string we have before failing + * outright. + */ + err = vhd_parent_locator_get(vhd, &image->parent); + if (!err) + return 0; + } + + loc = vhd_util_scan_get_parent_locator(vhd); + if (!loc) + return -EINVAL; + + return vhd_parent_locator_read(vhd, loc, &image->parent); +} + +static int +vhd_util_scan_get_hidden(vhd_context_t *vhd, struct vhd_image *image) +{ + int err, hidden; + + err = 0; + hidden = 0; + + if (target_vhd(image->target->type)) + err = vhd_hidden(vhd, &hidden); + else + hidden = 1; + + if (err) + return err; + + image->hidden = hidden; + return 0; +} + +static int +vhd_util_scan_get_size(vhd_context_t *vhd, struct vhd_image *image) +{ + image->size = image->target->size; + + if (target_vhd(image->target->type)) + image->capacity = vhd->footer.curr_size; + else + image->capacity = image->size; + + return 0; +} + +static int +vhd_util_scan_open_file(vhd_context_t *vhd, struct vhd_image *image) +{ + int err, vhd_flags; + + if (!target_vhd(image->target->type)) + return 0; + + vhd_flags = VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED; + if (flags & VHD_SCAN_FAST) + vhd_flags |= VHD_OPEN_FAST; + + err = vhd_open(vhd, image->name, vhd_flags); + if (err) { + vhd->file = NULL; + image->message = "opening file"; + image->error = err; + return image->error; + } + + return 0; +} + +static int +vhd_util_scan_read_volume_headers(vhd_context_t *vhd, struct vhd_image *image) +{ + int err; + char *buf; + size_t size; + struct target *target; + + buf = NULL; + target = image->target; + size = sizeof(vhd_footer_t) + sizeof(vhd_header_t); + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + buf = NULL; + image->message = "allocating image"; + image->error = -err; + goto out; + } + + err = vhd_seek(vhd, target->start, SEEK_SET); + if (err) { + image->message = "seeking to headers"; + image->error = err; + goto out; + } + + err = vhd_read(vhd, buf, size); + if (err) { + image->message = "reading headers"; + image->error = err; + goto out; + } + + memcpy(&vhd->footer, buf, sizeof(vhd_footer_t)); + vhd_footer_in(&vhd->footer); + err = vhd_validate_footer(&vhd->footer); + if (err) { + image->message = "invalid footer"; + image->error = err; + goto out; + } + + /* lvhd vhds should always be dynamic */ + if (vhd_type_dynamic(vhd)) { + if (vhd->footer.data_offset != sizeof(vhd_footer_t)) + err = vhd_read_header_at(vhd, &vhd->header, + vhd->footer.data_offset + + target->start); + else { + memcpy(&vhd->header, + buf + sizeof(vhd_footer_t), + sizeof(vhd_header_t)); + vhd_header_in(&vhd->header); + err = vhd_validate_header(&vhd->header); + } + + if (err) { + image->message = "reading header"; + image->error = err; + goto out; + } + + vhd->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT; + vhd->bm_secs = secs_round_up_no_zero(vhd->spb >> 3); + } + +out: + free(buf); + return image->error; +} + +static int +vhd_util_scan_open_volume(vhd_context_t *vhd, struct vhd_image *image) +{ + int err; + struct target *target; + + target = image->target; + memset(vhd, 0, sizeof(*vhd)); + vhd->oflags = VHD_OPEN_RDONLY | VHD_OPEN_FAST; + + if (target->end - target->start < 4096) { + image->message = "device too small"; + image->error = -EINVAL; + return image->error; + } + + vhd->file = strdup(image->name); + if (!vhd->file) { + image->message = "allocating device"; + image->error = -ENOMEM; + return image->error; + } + + vhd->fd = open(target->device, O_RDONLY | O_DIRECT | O_LARGEFILE); + if (vhd->fd == -1) { + free(vhd->file); + vhd->file = NULL; + + image->message = "opening device"; + image->error = -errno; + return image->error; + } + + if (target_vhd(target->type)) + return vhd_util_scan_read_volume_headers(vhd, image); + + return 0; +} + +static int +vhd_util_scan_open(vhd_context_t *vhd, struct vhd_image *image) +{ + struct target *target; + + target = image->target; + + if (target_volume(image->target->type) || !(flags & VHD_SCAN_PRETTY)) + image->name = target->name; + else { + image->name = realpath(target->name, NULL); + if (!image->name) { + image->name = target->name; + image->message = "resolving name"; + image->error = -errno; + return image->error; + } + } + + if (target_volume(target->type)) + return vhd_util_scan_open_volume(vhd, image); + else + return vhd_util_scan_open_file(vhd, image); +} + +static int +vhd_util_scan_init_file_target(struct target *target, + const char *file, uint8_t type) +{ + int err; + struct stat stats; + + err = stat(file, &stats); + if (err == -1) + return -errno; + + err = copy_name(target->name, file); + if (err) + return err; + + err = copy_name(target->device, file); + if (err) + return err; + + target->type = type; + target->start = 0; + target->size = stats.st_size; + target->end = stats.st_size; + + return 0; +} + +static int +vhd_util_scan_init_volume_target(struct target *target, + struct lv *lv, uint8_t type) +{ + int err; + + if (lv->first_segment.type != LVM_SEG_TYPE_LINEAR) + return -ENOSYS; + + err = copy_name(target->name, lv->name); + if (err) + return err; + + err = copy_name(target->device, lv->first_segment.device); + if (err) + return err; + + target->type = type; + target->size = lv->size; + target->start = lv->first_segment.pe_start; + target->end = target->start + lv->first_segment.pe_size; + + return 0; +} + +static int +iterator_init(struct iterator *itr, int cnt, struct target *targets) +{ + memset(itr, 0, sizeof(*itr)); + + itr->targets = malloc(sizeof(struct target) * cnt); + if (!itr->targets) + return -ENOMEM; + + memcpy(itr->targets, targets, sizeof(struct target) * cnt); + + itr->cur = 0; + itr->cur_size = cnt; + itr->max_size = cnt; + + return 0; +} + +static struct target * +iterator_next(struct iterator *itr) +{ + if (itr->cur == itr->cur_size) + return NULL; + + return itr->targets + itr->cur++; +} + +static int +iterator_add_file(struct iterator *itr, + struct target *target, const char *parent, uint8_t type) +{ + int i; + struct target *t; + char *lname, *rname; + + for (i = 0; i < itr->cur_size; i++) { + t = itr->targets + i; + lname = basename((char *)t->name); + rname = basename((char *)parent); + + if (!strcmp(lname, rname)) + return -EEXIST; + } + + return vhd_util_scan_init_file_target(target, parent, type); +} + +static int +iterator_add_volume(struct iterator *itr, + struct target *target, const char *parent, uint8_t type) +{ + int i, err; + struct lv *lv; + + lv = NULL; + err = -ENOENT; + + for (i = 0; i < itr->cur_size; i++) + if (!strcmp(parent, itr->targets[i].name)) + return -EEXIST; + + for (i = 0; i < vg.lv_cnt; i++) { + err = fnmatch(parent, vg.lvs[i].name, FNM_PATHNAME); + if (err != FNM_NOMATCH) { + lv = vg.lvs + i; + break; + } + } + + if (err && err != FNM_PATHNAME) + return err; + + if (!lv) + return -ENOENT; + + return vhd_util_scan_init_volume_target(target, lv, type); +} + +static int +iterator_add(struct iterator *itr, const char *parent, uint8_t type) +{ + int err; + struct target *target; + + if (itr->cur_size == itr->max_size) { + struct target *new; + + new = realloc(itr->targets, + sizeof(struct target) * + itr->max_size * 2); + if (!new) + return -ENOMEM; + + itr->max_size *= 2; + itr->targets = new; + } + + target = itr->targets + itr->cur_size; + + if (target_volume(type)) + err = iterator_add_volume(itr, target, parent, type); + else + err = iterator_add_file(itr, target, parent, type); + + if (err) + memset(target, 0, sizeof(*target)); + else + itr->cur_size++; + + return (err == -EEXIST ? 0 : err); +} + +static void +iterator_free(struct iterator *itr) +{ + free(itr->targets); + memset(itr, 0, sizeof(*itr)); +} + +static void +vhd_util_scan_add_parent(struct iterator *itr, + vhd_context_t *vhd, struct vhd_image *image) +{ + int err; + uint8_t type; + + if (vhd_parent_raw(vhd)) + type = target_volume(image->target->type) ? + VHD_TYPE_RAW_VOLUME : VHD_TYPE_RAW_FILE; + else + type = target_volume(image->target->type) ? + VHD_TYPE_VHD_VOLUME : VHD_TYPE_VHD_FILE; + + err = iterator_add(itr, image->parent, type); + if (err) + vhd_util_scan_error(image->parent, err); +} + +static int +vhd_util_scan_targets(int cnt, struct target *targets) +{ + int ret, err; + vhd_context_t vhd; + struct iterator itr; + struct target *target; + struct vhd_image image; + + ret = 0; + err = 0; + + err = iterator_init(&itr, cnt, targets); + if (err) + return err; + + while ((target = iterator_next(&itr))) { + memset(&vhd, 0, sizeof(vhd)); + memset(&image, 0, sizeof(image)); + + image.target = target; + + err = vhd_util_scan_open(&vhd, &image); + if (err) { + ret = -EAGAIN; + goto end; + } + + err = vhd_util_scan_get_size(&vhd, &image); + if (err) { + ret = -EAGAIN; + image.message = "getting physical size"; + image.error = err; + goto end; + } + + err = vhd_util_scan_get_hidden(&vhd, &image); + if (err) { + ret = -EAGAIN; + image.message = "checking 'hidden' field"; + image.error = err; + goto end; + } + + if (vhd.footer.type == HD_TYPE_DIFF) { + err = vhd_util_scan_get_parent(&vhd, &image); + if (err) { + ret = -EAGAIN; + image.message = "getting parent"; + image.error = err; + goto end; + } + } + + end: + vhd_util_scan_print_image(&image); + + if (flags & VHD_SCAN_PARENTS && image.parent) + vhd_util_scan_add_parent(&itr, &vhd, &image); + + if (vhd.file) + vhd_close(&vhd); + if (image.name != target->name) + free(image.name); + free(image.parent); + + if (err && !(flags & VHD_SCAN_NOFAIL)) + break; + } + + iterator_free(&itr); + + if (flags & VHD_SCAN_NOFAIL) + return ret; + + return err; +} + +static int +vhd_util_scan_targets_pretty(int cnt, struct target *targets) +{ + int err; + + err = vhd_util_scan_pretty_allocate_list(cnt); + if (err) { + printf("scan failed: no memory\n"); + return -ENOMEM; + } + + err = vhd_util_scan_targets(cnt, targets); + + vhd_util_scan_pretty_print_images(); + vhd_util_scan_pretty_free_list(); + + return ((flags & VHD_SCAN_NOFAIL) ? 0 : err); +} + +static int +vhd_util_scan_find_file_targets(int cnt, char **names, + const char *filter, + struct target **_targets, int *_total) +{ + glob_t g; + struct target *targets; + int i, globs, err, total; + + total = cnt; + globs = 0; + *_total = 0; + *_targets = NULL; + + memset(&g, 0, sizeof(g)); + + if (filter) { + int gflags = ((flags & VHD_SCAN_FAST) ? GLOB_NOSORT : 0); + + errno = 0; + err = glob(filter, gflags, vhd_util_scan_error, &g); + + switch (err) { + case GLOB_NOSPACE: + err = -ENOMEM; + break; + case GLOB_ABORTED: + err = -EIO; + break; + case GLOB_NOMATCH: + err = -errno; + break; + } + + if (err) { + vhd_util_scan_error(filter, err); + return err; + } + + globs = g.gl_pathc; + total += globs; + } + + targets = calloc(total, sizeof(struct target)); + if (!targets) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < g.gl_pathc; i++) { + err = vhd_util_scan_init_file_target(targets + i, + g.gl_pathv[i], + VHD_TYPE_VHD_FILE); + if (err) { + vhd_util_scan_error(g.gl_pathv[i], err); + if (!(flags & VHD_SCAN_NOFAIL)) + goto out; + } + } + + for (i = 0; i + globs < total; i++) { + err = vhd_util_scan_init_file_target(targets + i + globs, + names[i], + VHD_TYPE_VHD_FILE); + if (err) { + vhd_util_scan_error(names[i], err); + if (!(flags & VHD_SCAN_NOFAIL)) + goto out; + } + } + + err = 0; + *_total = total; + *_targets = targets; + +out: + if (err) + free(targets); + if (filter) + globfree(&g); + + return err; +} + +static inline void +swap_volume(struct lv *lvs, int dst, int src) +{ + struct lv copy, *ldst, *lsrc; + + if (dst == src) + return; + + lsrc = lvs + src; + ldst = lvs + dst; + + memcpy(©, ldst, sizeof(copy)); + memcpy(ldst, lsrc, sizeof(*ldst)); + memcpy(lsrc, ©, sizeof(copy)); +} + +static int +vhd_util_scan_sort_volumes(struct lv *lvs, int cnt, + const char *filter, int *_matches) +{ + struct lv *lv; + int i, err, matches; + + matches = 0; + *_matches = 0; + + if (!filter) + return 0; + + for (i = 0; i < cnt; i++) { + lv = lvs + i; + + err = fnmatch(filter, lv->name, FNM_PATHNAME); + if (err) { + if (err != FNM_NOMATCH) { + vhd_util_scan_error(lv->name, err); + if (!(flags & VHD_SCAN_NOFAIL)) + return err; + } + + continue; + } + + swap_volume(lvs, matches++, i); + } + + *_matches = matches; + return 0; +} + +static int +vhd_util_scan_find_volume_targets(int cnt, char **names, + const char *volume, const char *filter, + struct target **_targets, int *_total) +{ + struct target *targets; + int i, err, total, matches; + + *_total = 0; + *_targets = NULL; + targets = NULL; + + err = lvm_scan_vg(volume, &vg); + if (err) + return err; + + err = vhd_util_scan_sort_volumes(vg.lvs, vg.lv_cnt, + filter, &matches); + if (err) + goto out; + + total = matches; + for (i = 0; i < cnt; i++) { + err = vhd_util_scan_sort_volumes(vg.lvs + total, + vg.lv_cnt - total, + names[i], &matches); + if (err) + goto out; + + total += matches; + } + + targets = calloc(total, sizeof(struct target)); + if (!targets) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < total; i++) { + err = vhd_util_scan_init_volume_target(targets + i, + vg.lvs + i, + VHD_TYPE_VHD_VOLUME); + if (err) { + vhd_util_scan_error(vg.lvs[i].name, err); + if (!(flags & VHD_SCAN_NOFAIL)) + goto out; + } + } + + err = 0; + *_total = total; + *_targets = targets; + +out: + if (err) + free(targets); + return err; +} + +static int +vhd_util_scan_find_targets(int cnt, char **names, + const char *volume, const char *filter, + struct target **targets, int *total) +{ + if (flags & VHD_SCAN_VOLUME) + return vhd_util_scan_find_volume_targets(cnt, names, + volume, filter, + targets, total); + return vhd_util_scan_find_file_targets(cnt, names, + filter, targets, total); +} + +int +vhd_util_scan(int argc, char **argv) +{ + int c, ret, err, cnt; + char *filter, *volume; + struct target *targets; + + cnt = 0; + ret = 0; + err = 0; + flags = 0; + filter = NULL; + volume = NULL; + targets = NULL; + + optind = 0; + while ((c = getopt(argc, argv, "m:fcl:pavh")) != -1) { + switch (c) { + case 'm': + filter = optarg; + break; + case 'f': + flags |= VHD_SCAN_FAST; + break; + case 'c': + flags |= VHD_SCAN_NOFAIL; + break; + case 'l': + volume = optarg; + flags |= VHD_SCAN_VOLUME; + break; + case 'p': + flags |= VHD_SCAN_PRETTY; + break; + case 'a': + flags |= VHD_SCAN_PARENTS; + break; + case 'v': + flags |= VHD_SCAN_VERBOSE; + break; + case 'h': + goto usage; + default: + err = -EINVAL; + goto usage; + } + } + + if (!filter && argc - optind == 0) { + err = -EINVAL; + goto usage; + } + + if (flags & VHD_SCAN_PRETTY) + flags &= ~VHD_SCAN_FAST; + + err = vhd_util_scan_find_targets(argc - optind, argv + optind, + volume, filter, &targets, &cnt); + if (err) { + printf("scan failed: %d\n", err); + return err; + } + + if (!cnt) + return 0; + + if (flags & VHD_SCAN_PRETTY) + err = vhd_util_scan_targets_pretty(cnt, targets); + else + err = vhd_util_scan_targets(cnt, targets); + + free(targets); + lvm_free_vg(&vg); + + return ((flags & VHD_SCAN_NOFAIL) ? 0 : err); + +usage: + printf("usage: [OPTIONS] FILES\n" + "options: [-m match filter] [-f fast] [-c continue on failure] " + "[-l LVM volume] [-p pretty print] [-a scan parents] " + "[-v verbose] [-h help]\n"); + return err; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-set-field.c b/tools/vhd-tools/vhd/lib/vhd-util-set-field.c new file mode 100644 index 00000000000..32728abf8ab --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-set-field.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include + +#include "libvhd.h" + +int +vhd_util_set_field(int argc, char **argv) +{ + long value; + int err, c; + off_t eof; + vhd_context_t vhd; + char *name, *field; + + err = -EINVAL; + value = 0; + name = NULL; + field = NULL; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:f:v:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'f': + field = optarg; + break; + case 'v': + err = 0; + value = strtol(optarg, NULL, 10); + break; + case 'h': + default: + goto usage; + } + } + + if (!name || !field || optind != argc || err) + goto usage; + + if (strnlen(field, 25) >= 25) { + printf("invalid field\n"); + goto usage; + } + + if (strcmp(field, "hidden")) { + printf("invalid field %s\n", field); + goto usage; + } + + if (value < 0 || value > 255) { + printf("invalid value %ld\n", value); + goto usage; + } + + err = vhd_open(&vhd, name, VHD_OPEN_RDWR); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + vhd.footer.hidden = (char)value; + + err = vhd_write_footer(&vhd, &vhd.footer); + + done: + vhd_close(&vhd); + return err; + +usage: + printf("options: <-n name> <-f field> <-v value> [-h help]\n"); + return -EINVAL; +} diff --git a/tools/vhd-tools/vhd/lib/vhd-util-snapshot.c b/tools/vhd-tools/vhd/lib/vhd-util-snapshot.c new file mode 100644 index 00000000000..75960f96ea3 --- /dev/null +++ b/tools/vhd-tools/vhd/lib/vhd-util-snapshot.c @@ -0,0 +1,216 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include +#include + +#include "libvhd.h" + +static int +vhd_util_find_snapshot_target(const char *name, char **result, int *parent_raw) +{ + int i, err; + char *target; + vhd_context_t vhd; + + *parent_raw = 0; + *result = NULL; + + target = strdup(name); + if (!target) + return -ENOMEM; + + for (;;) { + err = vhd_open(&vhd, target, VHD_OPEN_RDONLY); + if (err) + return err; + + if (vhd.footer.type != HD_TYPE_DIFF) + goto out; + + err = vhd_get_bat(&vhd); + if (err) + goto out; + + for (i = 0; i < vhd.bat.entries; i++) + if (vhd.bat.bat[i] != DD_BLK_UNUSED) + goto out; + + free(target); + err = vhd_parent_locator_get(&vhd, &target); + if (err) + goto out; + + if (vhd_parent_raw(&vhd)) { + *parent_raw = 1; + goto out; + } + + vhd_close(&vhd); + } + +out: + vhd_close(&vhd); + if (err) + free(target); + else + *result = target; + + return err; +} + +static int +vhd_util_check_depth(const char *name, int *depth) +{ + int err; + vhd_context_t vhd; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY); + if (err) + return err; + + err = vhd_chain_depth(&vhd, depth); + vhd_close(&vhd); + + return err; +} + +int +vhd_util_snapshot(int argc, char **argv) +{ + vhd_flag_creat_t flags; + int c, err, prt_raw, limit; + char *name, *pname, *ppath, *backing; + uint64_t size; + vhd_context_t vhd; + + name = NULL; + pname = NULL; + ppath = NULL; + backing = NULL; + size = 0; + flags = 0; + limit = 0; + + if (!argc || !argv) { + err = -EINVAL; + goto usage; + } + + optind = 0; + while ((c = getopt(argc, argv, "n:p:l:mh")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'p': + pname = optarg; + break; + case 'l': + limit = strtol(optarg, NULL, 10); + break; + case 'm': + vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW); + break; + case 'h': + err = 0; + goto usage; + default: + err = -EINVAL; + goto usage; + } + } + + if (!name || !pname || optind != argc) { + err = -EINVAL; + goto usage; + } + + ppath = realpath(pname, NULL); + if (!ppath) + return -errno; + + if (vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW)) { + backing = strdup(ppath); + if (!backing) { + err = -ENOMEM; + goto out; + } + } else { + err = vhd_util_find_snapshot_target(ppath, &backing, &prt_raw); + if (err) { + backing = NULL; + goto out; + } + + /* + * if the sizes of the parent chain are non-uniform, we need to + * pick the right size: that of the supplied parent + */ + if (strcmp(ppath, backing)) { + err = vhd_open(&vhd, ppath, VHD_OPEN_RDONLY); + if (err) + goto out; + size = vhd.footer.curr_size; + vhd_close(&vhd); + } + + if (prt_raw) + vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW); + } + + if (limit && !vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW)) { + int depth; + + err = vhd_util_check_depth(backing, &depth); + if (err) + printf("error checking snapshot depth: %d\n", err); + else if (depth + 1 > limit) { + err = -ENOSPC; + printf("snapshot depth exceeded: " + "current depth: %d, limit: %d\n", depth, limit); + } + + if (err) + goto out; + } + + err = vhd_snapshot(name, size, backing, flags); + +out: + free(ppath); + free(backing); + + return err; + +usage: + printf("options: <-n name> <-p parent name> [-l snapshot depth limit]" + " [-m parent_is_raw] [-h help]\n"); + return err; +} diff --git a/tools/vhd-tools/vhd/vhd-update.c b/tools/vhd-tools/vhd/vhd-update.c new file mode 100644 index 00000000000..4621a81e209 --- /dev/null +++ b/tools/vhd-tools/vhd/vhd-update.c @@ -0,0 +1,259 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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. + * + * Before updating a VHD file, we create a journal consisting of: + * - all data at the beginning of the file, up to and including the BAT + * - each allocated bitmap (existing at the same offset in the journal as + * its corresponding bitmap in the original file) + * Updates are performed in place by writing appropriately + * transformed versions of journaled bitmaps to the original file. + */ +#include +#include +#include +#include +#include + +#include "atomicio.h" +#include "libvhd.h" +#include "libvhd-journal.h" + +static void +usage(void) +{ + printf("usage: vhd-update <-n name> [-j existing journal] [-h]\n"); + exit(EINVAL); +} + +/* + * update vhd creator version to reflect its new bitmap ordering + */ +static inline int +update_creator_version(vhd_journal_t *journal) +{ + journal->vhd.footer.crtr_ver = VHD_VERSION(1, 1); + return vhd_write_footer(&journal->vhd, &journal->vhd.footer); +} + +static int +journal_bitmaps(vhd_journal_t *journal) +{ + int i, err; + + for (i = 0; i < journal->vhd.bat.entries; i++) { + err = vhd_journal_add_block(journal, i, VHD_JOURNAL_METADATA); + if (err) + return err; + } + + return 0; +} + +/* + * older VHD bitmaps were little endian + * and bits within a word were set from right to left + */ +static inline int +old_test_bit(int nr, volatile void * addr) +{ + return (((unsigned long*)addr)[nr/(sizeof(unsigned long)*8)] >> + (nr % (sizeof(unsigned long)*8))) & 1; +} + +/* + * new VHD bitmaps are big endian + * and bits within a word are set from left to right + */ +#define BIT_MASK 0x80 +static inline void +new_set_bit (int nr, volatile char *addr) +{ + addr[nr >> 3] |= (BIT_MASK >> (nr & 7)); +} + +static void +convert_bitmap(char *in, char *out, int bytes) +{ + int i; + + memset(out, 0, bytes); + + for (i = 0; i < bytes << 3; i++) + if (old_test_bit(i, (void *)in)) + new_set_bit(i, out); +} + +static int +update_vhd(vhd_journal_t *journal, int rollback) +{ + int i, err; + size_t size; + char *buf, *converted; + + buf = NULL; + converted = NULL; + + size = vhd_bytes_padded(journal->vhd.spb / 8); + err = posix_memalign((void **)&converted, 512, size); + if (err) { + converted = NULL; + goto out; + } + + for (i = 0; i < journal->vhd.bat.entries; i++) { + if (journal->vhd.bat.bat[i] == DD_BLK_UNUSED) + continue; + + err = vhd_read_bitmap(&journal->vhd, i, &buf); + if (err) + goto out; + + if (rollback) + memcpy(converted, buf, size); + else + convert_bitmap(buf, converted, size); + + free(buf); + + err = vhd_write_bitmap(&journal->vhd, i, converted); + if (err) + goto out; + } + + err = 0; + out: + free(converted); + return err; +} + +static int +open_journal(vhd_journal_t *journal, const char *file, const char *jfile) +{ + int err; + + err = vhd_journal_create(journal, file, jfile); + if (err) { + printf("error creating journal for %s: %d\n", file, err); + return err; + } + + return 0; +} + +static int +close_journal(vhd_journal_t *journal, int err) +{ + if (err) + err = vhd_journal_revert(journal); + else + err = vhd_journal_commit(journal); + + if (err) + return vhd_journal_close(journal); + else + return vhd_journal_remove(journal); +} + +int +main(int argc, char **argv) +{ + char *file, *jfile; + int c, err, rollback; + vhd_journal_t journal; + + file = NULL; + jfile = NULL; + rollback = 0; + + while ((c = getopt(argc, argv, "n:j:rh")) != -1) { + switch(c) { + case 'n': + file = optarg; + break; + case 'j': + jfile = optarg; + err = access(jfile, R_OK); + if (err == -1) { + printf("invalid journal arg %s\n", jfile); + return -errno; + } + break; + case 'r': + /* add a rollback option for debugging which + * pushes journalled bitmaps to original file + * without transforming them */ + rollback = 1; + break; + default: + usage(); + } + } + + if (!file) + usage(); + + if (rollback && !jfile) { + printf("rollback requires a journal argument\n"); + usage(); + } + + err = open_journal(&journal, file, jfile); + if (err) + return err; + + if (!vhd_creator_tapdisk(&journal.vhd) || + journal.vhd.footer.crtr_ver != VHD_VERSION(0, 1) || + journal.vhd.footer.type == HD_TYPE_FIXED) { + err = 0; + goto out; + } + + err = journal_bitmaps(&journal); + if (err) { + /* no changes to vhd file yet, + * so close the journal and bail */ + vhd_journal_close(&journal); + return err; + } + + err = update_vhd(&journal, rollback); + if (err) { + printf("update failed: %d; saving journal\n", err); + goto out; + } + + err = update_creator_version(&journal); + if (err) { + printf("failed to udpate creator version: %d\n", err); + goto out; + } + + err = 0; + +out: + err = close_journal(&journal, err); + return err; +} diff --git a/tools/vhd-tools/vhd/vhd-util.c b/tools/vhd-tools/vhd/vhd-util.c new file mode 100644 index 00000000000..944a59e395d --- /dev/null +++ b/tools/vhd-tools/vhd/vhd-util.c @@ -0,0 +1,160 @@ +/* Copyright (c) 2008, XenSource Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER + * 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 +#include +#include +#include + +#include "libvhd.h" +#include "vhd-util.h" + +#if 1 +#define DFPRINTF(_f, _a...) fprintf(stdout, _f , ##_a) +#else +#define DFPRINTF(_f, _a...) ((void)0) +#endif + +typedef int (*vhd_util_func_t) (int, char **); + +struct command { + char *name; + vhd_util_func_t func; +}; + +struct command commands[] = { + { .name = "create", .func = vhd_util_create }, + { .name = "snapshot", .func = vhd_util_snapshot }, + { .name = "query", .func = vhd_util_query }, + { .name = "read", .func = vhd_util_read }, + { .name = "set", .func = vhd_util_set_field }, + { .name = "repair", .func = vhd_util_repair }, + { .name = "resize", .func = vhd_util_resize }, + { .name = "fill", .func = vhd_util_fill }, + { .name = "coalesce", .func = vhd_util_coalesce }, + { .name = "modify", .func = vhd_util_modify }, + { .name = "scan", .func = vhd_util_scan }, + { .name = "check", .func = vhd_util_check }, + { .name = "revert", .func = vhd_util_revert }, +}; + +#define print_commands() \ + do { \ + int i, n; \ + n = sizeof(commands) / sizeof(struct command); \ + printf("COMMAND := { "); \ + printf("%s", commands[0].name); \ + for (i = 1; i < n; i++) \ + printf(" | %s", commands[i].name); \ + printf(" }\n"); \ + } while (0) + +TEST_FAIL_EXTERN_VARS; + +void +help(void) +{ + printf("usage: vhd-util COMMAND [OPTIONS]\n"); + print_commands(); + exit(0); +} + +struct command * +get_command(char *command) +{ + int i, n; + + if (strnlen(command, 25) >= 25) + return NULL; + + n = sizeof(commands) / sizeof (struct command); + + for (i = 0; i < n; i++) + if (!strcmp(command, commands[i].name)) + return &commands[i]; + + return NULL; +} + +int +main(int argc, char *argv[]) +{ + char **cargv; + struct command *cmd; + int cargc, i, cnt, ret; + +#ifdef CORE_DUMP + #include + struct rlimit rlim; + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &rlim) < 0) + fprintf(stderr, "setrlimit failed: %d\n", errno); +#endif + + ret = 0; + + if (argc < 2) + help(); + + cargc = argc - 1; + cmd = get_command(argv[1]); + if (!cmd) { + fprintf(stderr, "invalid COMMAND %s\n", argv[1]); + help(); + } + + cargv = malloc(sizeof(char *) * cargc); + if (!cargv) + exit(ENOMEM); + + cnt = 1; + cargv[0] = cmd->name; + for (i = 1; i < cargc; i++) { + char *arg = argv[i + (argc - cargc)]; + + if (!strcmp(arg, "--debug")) { + libvhd_set_log_level(1); + continue; + } + + cargv[cnt++] = arg; + } + +#ifdef ENABLE_FAILURE_TESTING + for (i = 0; i < NUM_FAIL_TESTS; i++) { + TEST_FAIL[i] = 0; + if (getenv(ENV_VAR_FAIL[i])) + TEST_FAIL[i] = 1; + } +#endif // ENABLE_FAILURE_TESTING + + ret = cmd->func(cnt, cargv); + + free(cargv); + + return (ret >= 0 ? ret : -ret); +}