1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2007, Marcus Overhagen. All Rights Reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include <errno.h>
8#include <fcntl.h>
9#include <getopt.h>
10#include <limits.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/stat.h>
15#include <unistd.h>
16
17
18#define EXIT_FAILURE 1
19
20
21static void
22print_usage(bool error)
23{
24	printf("\n");
25	printf("create_image\n");
26	printf("\n");
27	printf("usage: create_image -i <imagesize> [-c] [-f] <file>\n");
28	printf("       -i, --imagesize    size of raw partition image file\n");
29	printf("       -f, --file         the raw partition image file\n");
30	printf("       -c, --clear-image  set the image content to zero\n");
31	exit(error ? EXIT_FAILURE : 0);
32}
33
34
35int
36main(int argc, char *argv[])
37{
38	off_t imageSize = 0;
39	const char *file = NULL;
40	bool clearImage = false;
41
42	while (1) {
43		int c;
44		static struct option long_options[] = {
45			{"file", required_argument, 0, 'f'},
46			{"clear-image", no_argument, 0, 'c'},
47			{"help", no_argument, 0, 'h'},
48			{"imagesize", required_argument, 0, 'i'},
49			{0, 0, 0, 0}
50		};
51
52		opterr = 0; /* don't print errors */
53		c = getopt_long(argc, argv, "+hi:cf:", long_options, NULL);
54		if (c == -1)
55			break;
56
57		switch (c) {
58			case 'h':
59				print_usage(false);
60				break;
61
62			case 'i':
63				imageSize = strtoull(optarg, NULL, 10);
64				if (strchr(optarg, 'G') || strchr(optarg, 'g'))
65					imageSize *= 1024 * 1024 * 1024;
66				else if (strchr(optarg, 'M') || strchr(optarg, 'm'))
67					imageSize *= 1024 * 1024;
68				else if (strchr(optarg, 'K') || strchr(optarg, 'k'))
69					imageSize *= 1024;
70				break;
71
72			case 'f':
73				file = optarg;
74				break;
75
76			case 'c':
77				clearImage = true;
78				break;
79
80			default:
81				print_usage(true);
82		}
83	}
84
85	if (file == NULL && optind == argc - 1)
86		file = argv[optind];
87
88	if (!imageSize || !file)
89		print_usage(true);
90
91	if (imageSize < 0) {
92		fprintf(stderr, "Error: invalid image size\n");
93		exit(EXIT_FAILURE);
94	}
95
96	if (imageSize % 512) {
97		fprintf(stderr, "Error: image size must be a multiple of 512 bytes\n");
98		exit(EXIT_FAILURE);
99	}
100
101	int fd = open(file, O_RDWR | O_CREAT, 0666);
102	if (fd < 0) {
103		fprintf(stderr, "Error: couldn't open file %s (%s)\n", file,
104			strerror(errno));
105		exit(EXIT_FAILURE);
106	}
107
108	struct stat st;
109	if (fstat(fd, &st) < 0) {
110		fprintf(stderr, "Error: stat()ing file %s failed (%s)\n", file,
111			strerror(errno));
112		exit(EXIT_FAILURE);
113	}
114
115	if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) {
116		fprintf(stderr, "Error: type of file %s not supported\n", file);
117		exit(EXIT_FAILURE);
118	}
119
120	if (S_ISREG(st.st_mode)) {
121		// regular file -- use ftruncate() to resize it
122		if ((clearImage && ftruncate(fd, 0) != 0)
123			|| ftruncate(fd, imageSize) != 0) {
124			fprintf(stderr, "Error: resizing file %s failed (%s)\n", file,
125				strerror(errno));
126			exit(EXIT_FAILURE);
127		}
128	} else {
129		// some kind of device -- clear it manually, if we have to
130		if (clearImage) {
131			char buffer[1024 * 1024];
132			memset(buffer, 0, sizeof(buffer));
133
134			off_t totalWritten = 0;
135			ssize_t written;
136			while ((written = write(fd, buffer, sizeof(buffer))) > 0)
137				totalWritten += written;
138
139			// Only fail, if an error occurs and we haven't written anything at
140			// all yet.
141			// TODO: We should probably first determine the size of the device
142			// and try to write only that much.
143			if (totalWritten == 0 && written < 0) {
144				fprintf(stderr, "Error: writing to device file %s failed "
145					"(%s)\n", file, strerror(errno));
146				exit(EXIT_FAILURE);
147			}
148		}
149	}
150
151	close(fd);
152	return 0;
153}
154