1 /* Copyright (c) 2008, XenSource Inc.
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *     * Redistributions of source code must retain the above copyright
7  *       notice, this list of conditions and the following disclaimer.
8  *     * Redistributions in binary form must reproduce the above copyright
9  *       notice, this list of conditions and the following disclaimer in the
10  *       documentation and/or other materials provided with the distribution.
11  *     * Neither the name of XenSource Inc. nor the names of its contributors
12  *       may be used to endorse or promote products derived from this software
13  *       without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
19  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * Before updating a VHD file, we create a journal consisting of:
28  *   - all data at the beginning of the file, up to and including the BAT
29  *   - each allocated bitmap (existing at the same offset in the journal as
30  *                            its corresponding bitmap in the original file)
31  * Updates are performed in place by writing appropriately
32  * transformed versions of journaled bitmaps to the original file.
33  */
34 #include <stdio.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 
40 #include "atomicio.h"
41 #include "libvhd.h"
42 #include "libvhd-journal.h"
43 
44 static void
usage(void)45 usage(void)
46 {
47 	printf("usage: vhd-update <-n name> [-j existing journal] [-h]\n");
48 	exit(EINVAL);
49 }
50 
51 /*
52  * update vhd creator version to reflect its new bitmap ordering
53  */
54 static inline int
update_creator_version(vhd_journal_t * journal)55 update_creator_version(vhd_journal_t *journal)
56 {
57 	journal->vhd.footer.crtr_ver = VHD_VERSION(1, 1);
58 	return vhd_write_footer(&journal->vhd, &journal->vhd.footer);
59 }
60 
61 static int
journal_bitmaps(vhd_journal_t * journal)62 journal_bitmaps(vhd_journal_t *journal)
63 {
64 	int i, err;
65 
66 	for (i = 0; i < journal->vhd.bat.entries; i++) {
67 		err = vhd_journal_add_block(journal, i, VHD_JOURNAL_METADATA);
68 		if (err)
69 			return err;
70 	}
71 
72 	return 0;
73 }
74 
75 /*
76  * older VHD bitmaps were little endian
77  * and bits within a word were set from right to left
78  */
79 static inline int
old_test_bit(int nr,volatile void * addr)80 old_test_bit(int nr, volatile void * addr)
81 {
82         return (((unsigned long*)addr)[nr/(sizeof(unsigned long)*8)] >>
83                 (nr % (sizeof(unsigned long)*8))) & 1;
84 }
85 
86 /*
87  * new VHD bitmaps are big endian
88  * and bits within a word are set from left to right
89  */
90 #define BIT_MASK 0x80
91 static inline void
new_set_bit(int nr,volatile char * addr)92 new_set_bit (int nr, volatile char *addr)
93 {
94         addr[nr >> 3] |= (BIT_MASK >> (nr & 7));
95 }
96 
97 static void
convert_bitmap(char * in,char * out,int bytes)98 convert_bitmap(char *in, char *out, int bytes)
99 {
100 	int i;
101 
102 	memset(out, 0, bytes);
103 
104 	for (i = 0; i < bytes << 3; i++)
105 		if (old_test_bit(i, (void *)in))
106 			new_set_bit(i, out);
107 }
108 
109 static int
update_vhd(vhd_journal_t * journal,int rollback)110 update_vhd(vhd_journal_t *journal, int rollback)
111 {
112 	int i, err;
113 	size_t size;
114 	char *buf, *converted;
115 
116 	buf       = NULL;
117 	converted = NULL;
118 
119 	size = vhd_bytes_padded(journal->vhd.spb / 8);
120 	err  = posix_memalign((void **)&converted, 512, size);
121 	if (err) {
122 		converted = NULL;
123 		goto out;
124 	}
125 
126 	for (i = 0; i < journal->vhd.bat.entries; i++) {
127 		if (journal->vhd.bat.bat[i] == DD_BLK_UNUSED)
128 			continue;
129 
130 		err = vhd_read_bitmap(&journal->vhd, i, &buf);
131 		if (err)
132 			goto out;
133 
134 		if (rollback)
135 			memcpy(converted, buf, size);
136 		else
137 			convert_bitmap(buf, converted, size);
138 
139 		free(buf);
140 
141 		err = vhd_write_bitmap(&journal->vhd, i, converted);
142 		if (err)
143 			goto out;
144 	}
145 
146 	err = 0;
147  out:
148 	free(converted);
149 	return err;
150 }
151 
152 static int
open_journal(vhd_journal_t * journal,const char * file,const char * jfile)153 open_journal(vhd_journal_t *journal, const char *file, const char *jfile)
154 {
155 	int err;
156 
157 	err = vhd_journal_create(journal, file, jfile);
158 	if (err) {
159 		printf("error creating journal for %s: %d\n", file, err);
160 		return err;
161 	}
162 
163 	return 0;
164 }
165 
166 static int
close_journal(vhd_journal_t * journal,int err)167 close_journal(vhd_journal_t *journal, int err)
168 {
169 	if (err)
170 		err = vhd_journal_revert(journal);
171 	else
172 		err = vhd_journal_commit(journal);
173 
174 	if (err)
175 		return vhd_journal_close(journal);
176 	else
177 		return vhd_journal_remove(journal);
178 }
179 
180 int
main(int argc,char ** argv)181 main(int argc, char **argv)
182 {
183 	char *file, *jfile;
184 	int c, err, rollback;
185 	vhd_journal_t journal;
186 
187 	file     = NULL;
188 	jfile    = NULL;
189 	rollback = 0;
190 
191 	while ((c = getopt(argc, argv, "n:j:rh")) != -1) {
192 		switch(c) {
193 		case 'n':
194 			file = optarg;
195 			break;
196 		case 'j':
197 			jfile = optarg;
198 			err = access(jfile, R_OK);
199 			if (err == -1) {
200 				printf("invalid journal arg %s\n", jfile);
201 				return -errno;
202 			}
203 			break;
204 		case 'r':
205 			/* add a rollback option for debugging which
206 			 * pushes journalled bitmaps to original file
207 			 * without transforming them */
208 			rollback = 1;
209 			break;
210 		default:
211 			usage();
212 		}
213 	}
214 
215 	if (!file)
216 		usage();
217 
218 	if (rollback && !jfile) {
219 		printf("rollback requires a journal argument\n");
220 		usage();
221 	}
222 
223 	err = open_journal(&journal, file, jfile);
224 	if (err)
225 		return err;
226 
227 	if (!vhd_creator_tapdisk(&journal.vhd) ||
228 	    journal.vhd.footer.crtr_ver != VHD_VERSION(0, 1) ||
229 	    journal.vhd.footer.type == HD_TYPE_FIXED) {
230 		err = 0;
231 		goto out;
232 	}
233 
234 	err = journal_bitmaps(&journal);
235 	if (err) {
236 		/* no changes to vhd file yet,
237 		 * so close the journal and bail */
238 		vhd_journal_close(&journal);
239 		return err;
240 	}
241 
242 	err = update_vhd(&journal, rollback);
243 	if (err) {
244 		printf("update failed: %d; saving journal\n", err);
245 		goto out;
246 	}
247 
248 	err = update_creator_version(&journal);
249 	if (err) {
250 		printf("failed to udpate creator version: %d\n", err);
251 		goto out;
252 	}
253 
254 	err = 0;
255 
256 out:
257 	err = close_journal(&journal, err);
258 	return err;
259 }
260