xref: /haiku/src/tools/vmdkimage/vmdkimage.cpp (revision 4b3b81da9e459443d75329cfd08bc9a57ad02653)
1 /*
2  * Copyright 2007, Marcus Overhagen. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <stdarg.h>
10 #include <errno.h>
11 #include <getopt.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include "vmdkimage.h"
17 
18 
19 static void
20 print_usage()
21 {
22 	printf("\n");
23 	printf("vmdkimage\n");
24 	printf("\n");
25 	printf("usage: vmdkimage -i <imagesize> -h <headersize> [-c] [-H] [-f] "
26 		"<file>\n");
27 	printf("       -i, --imagesize    size of raw partition image file\n");
28 	printf("       -h, --headersize   size of the vmdk header to write\n");
29 	printf("       -f, --file         the raw partition image file\n");
30 	printf("       -c, --clear-image  set the image content to zero\n");
31 	printf("       -H, --header-only  write only the header\n");
32 	exit(EXIT_FAILURE);
33 }
34 
35 
36 int
37 main(int argc, char *argv[])
38 {
39 	uint64 headersize = 0;
40 	uint64 imagesize = 0;
41 	const char *file = NULL;
42 	bool headerOnly = false;
43 	bool clearImage = false;
44 
45 	if (sizeof(SparseExtentHeader) != 512) {
46 		fprintf(stderr, "compilation error: struct size is %u byte\n",
47 			(unsigned)sizeof(SparseExtentHeader));
48 		exit(EXIT_FAILURE);
49 	}
50 
51 	while (1) {
52 		int c;
53 		static struct option long_options[] =
54 		{
55 		 	{"headersize", required_argument, 0, 'h'},
56 			{"imagesize", required_argument, 0, 'i'},
57 			{"file", required_argument, 0, 'f'},
58 			{"clear-image", no_argument, 0, 'c'},
59 			{"header-only", no_argument, 0, 'H'},
60 			{0, 0, 0, 0}
61 		};
62 
63 		opterr = 0; /* don't print errors */
64 		c = getopt_long(argc, argv, "h:i:cHf:", long_options, NULL);
65 		if (c == -1)
66 			break;
67 
68 		switch (c) {
69 			case 'h':
70 				headersize = strtoull(optarg, NULL, 10);
71 				if (strchr(optarg, 'G') || strchr(optarg, 'g'))
72 					headersize *= 1024 * 1024 * 1024;
73 				else if (strchr(optarg, 'M') || strchr(optarg, 'm'))
74 					headersize *= 1024 * 1024;
75 				else if (strchr(optarg, 'K') || strchr(optarg, 'k'))
76 					headersize *= 1024;
77 				break;
78 
79 			case 'i':
80 				imagesize = strtoull(optarg, NULL, 10);
81 				if (strchr(optarg, 'G') || strchr(optarg, 'g'))
82 					imagesize *= 1024 * 1024 * 1024;
83 				else if (strchr(optarg, 'M') || strchr(optarg, 'm'))
84 					imagesize *= 1024 * 1024;
85 				else if (strchr(optarg, 'K') || strchr(optarg, 'k'))
86 					imagesize *= 1024;
87 				break;
88 
89 			case 'f':
90 				file = optarg;
91 				break;
92 
93 			case 'c':
94 				clearImage = true;
95 				break;
96 
97 			case 'H':
98 				headerOnly = true;
99 				break;
100 
101 			default:
102 				print_usage();
103 		}
104 	}
105 
106 	if (file == NULL && optind == argc - 1)
107 		file = argv[optind];
108 
109 	if (!headersize || !imagesize || !file)
110 		print_usage();
111 
112 	char desc[512];
113 	SparseExtentHeader header;
114 
115 	if (headersize < sizeof(desc) + sizeof(header)) {
116 		fprintf(stderr, "Error: header size must be at least %u byte\n",
117 			(unsigned)(sizeof(desc) + sizeof(header)));
118 		exit(EXIT_FAILURE);
119 	}
120 
121 	if (headersize % 512) {
122 		fprintf(stderr, "Error: header size must be a multiple of 512 bytes\n");
123 		exit(EXIT_FAILURE);
124 	}
125 
126 	if (imagesize % 512) {
127 		fprintf(stderr, "Error: image size must be a multiple of 512 bytes\n");
128 		exit(EXIT_FAILURE);
129 	}
130 
131 	// arbitrary 1 GB limitation
132 	if (headersize > 0x40000000u) {
133 		fprintf(stderr, "Error: header size too large\n");
134 		exit(EXIT_FAILURE);
135 	}
136 
137 	// arbitrary 2 GB limitation
138 	if (imagesize > 0x80000000u) {
139 		fprintf(stderr, "Error: image size too large\n");
140 		exit(EXIT_FAILURE);
141 	}
142 
143 	const char *name = strrchr(file, '/');
144 	name = name ? (name + 1) : file;
145 
146 //	printf("headersize %llu\n", headersize);
147 //	printf("imagesize %llu\n", imagesize);
148 //	printf("file %s\n", file);
149 
150 	uint64 sectors;
151 	uint64 heads;
152 	uint64 cylinders;
153 
154 	// TODO: fixme!
155 	sectors = 63;
156 	heads = 16;
157 	cylinders = imagesize / (sectors * heads * 512);
158 	while (cylinders > 1024) {
159 		cylinders /= 2;
160 		heads *= 2;
161 	}
162 	off_t actualImageSize = (off_t)cylinders * sectors * heads * 512;
163 
164 	memset(desc, 0, sizeof(desc));
165 	memset(&header, 0, sizeof(header));
166 
167 	header.magicNumber = SPARSE_MAGICNUMBER;
168 	header.version = 1;
169 	header.flags = 1;
170 	header.capacity = 0;
171 	header.grainSize = 16;
172 	header.descriptorOffset = 1;
173 	header.descriptorSize = sizeof(desc) / 512;
174 	header.numGTEsPerGT = 512;
175 	header.rgdOffset = 0;
176 	header.gdOffset = 0;
177 	header.overHead = headersize / 512;
178 	header.uncleanShutdown = 0;
179 	header.singleEndLineChar = '\n';
180 	header.nonEndLineChar = ' ';
181 	header.doubleEndLineChar1 = '\r';
182 	header.doubleEndLineChar2 = '\n';
183 
184 	strcat(desc,
185 		"# Disk Descriptor File\n"
186 		"version=1\n"
187 		"CID=fffffffe\n"
188 		"parentCID=ffffffff\n"
189 		"createType=\"monolithicFlat\"\n");
190 	sprintf(desc + strlen(desc),
191 		"# Extent Description\n"
192 		"RW %llu FLAT \"%s\" %llu\n",
193 		actualImageSize / 512, name, headersize / 512);
194 	sprintf(desc + strlen(desc),
195 		"# Disk Data Base\n"
196 		"ddb.toolsVersion = \"0\"\n"
197 		"ddb.virtualHWVersion = \"3\"\n"
198 		"ddb.geometry.sectors = \"%llu\"\n"
199 		"ddb.adapterType = \"ide\"\n"
200 		"ddb.geometry.heads = \"%llu\"\n"
201 		"ddb.geometry.cylinders = \"%llu\"\n",
202 		sectors, heads, cylinders);
203 
204 	int fd = open(file, O_RDWR | O_CREAT, 0666);
205 	if (fd < 0) {
206 		fprintf(stderr, "Error: couldn't open file %s (%s)\n", file,
207 			strerror(errno));
208 		exit(EXIT_FAILURE);
209 	}
210 	if (sizeof(header) != write(fd, &header, sizeof(header)))
211 		goto write_err;
212 
213 	if (sizeof(desc) != write(fd, desc, sizeof(desc)))
214 		goto write_err;
215 
216 	headersize--;
217 	if (headersize != (uint64)lseek(fd, headersize, SEEK_SET))
218 		goto write_err;
219 
220 	if (1 != write(fd, "", 1))
221 		goto write_err;
222 
223 	if (!headerOnly) {
224 		if (clearImage && ftruncate(fd, headersize) != 0
225 			|| ftruncate(fd, actualImageSize + headersize)  != 0) {
226 			fprintf(stderr, "Error: resizing file %s failed (%s)\n", file,
227 				strerror(errno));
228 			exit(EXIT_FAILURE);
229 		}
230 	}
231 
232 	close(fd);
233 	return 0;
234 
235 write_err:
236 	fprintf(stderr, "Error: writing file %s failed (%s)\n", file,
237 		strerror(errno));
238 	close(fd);
239 	exit(EXIT_FAILURE);
240 }
241