summaryrefslogtreecommitdiff
path: root/mkubootenv.c
blob: 7c9922c563585f6069c484b5c69ff1ff5e703a80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/*
 * Copyright (c) 2009, Zurich University of Applied Science
 * Copyright (c) 2009, Tobias Klauser <klto@zhaw.ch>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include "crc32.h"

#define DEBUG

#define CMD_NAME		"mkubootenv"

#define CRC32_SIZE		sizeof(uint32_t)
/* minimum trailing null bytes */
#define TRAILER_SIZE		2

#define err(fmt, args...)	fprintf(stderr, "%s: Error: " fmt, CMD_NAME, ##args)
#ifdef DEBUG
# define dbg(fmt, args...)	fprintf(stdout, fmt, ##args)
#else
# define dbg(fmt, args...)
#endif

/* file "object" */
struct file {
	char *name;
	int fd;
	uint8_t *ptr;
	struct stat sbuf;
};

static void usage_and_exit(int status) __attribute__((noreturn));

static void usage_and_exit(int status)
{
	printf("usage: mkenv [-t <type>] [-s <size>] <source file> <target image file>\n"
	       "  -s <size>  set size of the target image file to <size> bytes. If <size> is\n"
	       "             bigger than the source file, the target image gets padded with null\n"
	       "             bytes. If <size> is smaller than the source file, an error is emitted.\n"
	       "  -r         reverse operation: get source file from target image file\n");
	exit(status);
}

int main(int argc, char **argv)
{
	int i;
	int status = EXIT_FAILURE;
	struct file s, t;	/* source and target file */
	ssize_t img_size = -1;
	ssize_t min_img_size;
	bool reverse = false;
	uint8_t *p, *q;
	uint32_t *crc;

	if (argc < 2)
		usage_and_exit(EXIT_FAILURE);

	/* parse commandline options */
	for (i = 1; (i + 1 < argc) && (argv[i][0] == '-'); i++) {
		switch (argv[i][1]) {
		case 's':
			img_size = strtol(argv[++i], NULL, 10);
			break;
		case 'r':
			reverse = true;
			break;
		case 'h':
			status = EXIT_SUCCESS;
			/* fall through */
		default:
			usage_and_exit(status);
			break;
		}
	}

	/* we expect two filenames */
	if (i + 2 > argc)
		usage_and_exit(EXIT_FAILURE);

	s.name = argv[i];
	t.name = argv[++i];

	s.fd = open(s.name, O_RDONLY);
	if (s.fd < 0) {
		err("Can't open source file '%s': %s\n", s.name,
				strerror(errno));
		exit(EXIT_FAILURE);
	}

	if (fstat(s.fd, &s.sbuf) < 0) {
		err("Can't stat source file '%s': %s\n", s.name,
				strerror(errno));
		goto err_out_close_s;
	}

	s.ptr = mmap(NULL, s.sbuf.st_size, PROT_READ, MAP_SHARED, s.fd, 0);
	if (s.ptr == MAP_FAILED) {
		err("Can't mmap source image file '%s': %s\n", s.name,
				strerror(errno));
		goto err_out_close_s;
	}

	/* TODO: Check source file format:
	 *	var=value\n
	 */

	/* check whether the size hasn't been set or whether the source file +
	 * CRC + trailer fits into the specified size.
	 */
	min_img_size = CRC32_SIZE + s.sbuf.st_size + TRAILER_SIZE;
	if (img_size < 0)
		img_size = min_img_size;
	else if (img_size < min_img_size) {
		err("Specified size (%zd) is too small for the source file to "
				"fit into. Must be at least %zd bytes.\n",
				img_size, min_img_size);
		goto err_out_munmap_s;
	}

	t.fd = open(t.name, O_RDWR|O_CREAT|O_TRUNC, 0666);
	if (t.fd < 0) {
		err("Can't open target image file '%s': %s\n", t.name,
				strerror(errno));
		goto err_out_munmap_s;
	}

	/* seek to the end of the target file to write a byte */
	if (lseek(t.fd, img_size - 1, SEEK_SET) < 0) {
		err("Can't seek in target image file '%s': %s\n", t.name,
				strerror(errno));
		goto err_out_close_t;
	}

	if (write(t.fd, "", 1) < 0) {
		err("Can't write to target image file '%s': %s\n", t.name,
				strerror(errno));
		goto err_out_close_t;
	}

	t.ptr = mmap(NULL, img_size, PROT_READ|PROT_WRITE, MAP_SHARED, t.fd, 0);
	if (t.ptr == MAP_FAILED) {
		err("Can't mmap target image file '%s': %s\n", t.name,
				strerror(errno));
		goto err_out_close_t;
	}

	dbg("source file:       %s\n", s.name);
	dbg("target image file: %s\n", t.name);
	dbg("size:              %zd\n", img_size);

	/* CRC32 placeholder, will be filled later */
	dbg("writing crc...\n");
	for (p = t.ptr; p < t.ptr + CRC32_SIZE; p++)
		*p = 0;

	/* copy the source file, replacing \n by \0 */
	dbg("writing data...\n");
	for (q = s.ptr; q < s.ptr + s.sbuf.st_size; q++, p++) {
		uint8_t c = (*q == '\n') ? '\0' : *q;

		*p = c;
	}

	/* trailer */
	dbg("writing trailer...\n");
	for (q = s.ptr + s.sbuf.st_size; q < s.ptr + img_size; q++)
		*p = 0;

	/* now for the real CRC32 */
	dbg("calculating crc...\n");
	crc = (uint32_t *) t.ptr;
	*crc = crc32(0, t.ptr + CRC32_SIZE, img_size - CRC32_SIZE);
	dbg("crc: %08x\n", *crc);

	status = EXIT_SUCCESS;

	munmap(t.ptr, img_size);
err_out_close_t:
	close(t.fd);
err_out_munmap_s:
	munmap(s.ptr, s.sbuf.st_size);
err_out_close_s:
	close(s.fd);

	exit(status);
}