1// SPDX-License-Identifier: MIT
2/*
3 * Utility functions for the 'fsverity' program
4 *
5 * Copyright 2018 Google LLC
6 *
7 * Use of this source code is governed by an MIT-style
8 * license that can be found in the LICENSE file or at
9 * https://opensource.org/licenses/MIT.
10 */
11
12#include "utils.h"
13
14#include <errno.h>
15#include <fcntl.h>
16#include <inttypes.h>
17#include <limits.h>
18#include <stdarg.h>
19#include <sys/stat.h>
20#include <unistd.h>
21#ifdef _WIN32
22#  include <windows.h>
23#endif
24
25/* ========== Memory allocation ========== */
26
27void *xmalloc(size_t size)
28{
29	void *p = malloc(size);
30
31	if (!p)
32		fatal_error("out of memory");
33	return p;
34}
35
36void *xzalloc(size_t size)
37{
38	return memset(xmalloc(size), 0, size);
39}
40
41void *xmemdup(const void *mem, size_t size)
42{
43	return memcpy(xmalloc(size), mem, size);
44}
45
46char *xstrdup(const char *s)
47{
48	return xmemdup(s, strlen(s) + 1);
49}
50
51/* ========== Error messages and assertions ========== */
52
53static void do_error_msg(const char *format, va_list va, int err)
54{
55	fputs("ERROR: ", stderr);
56	vfprintf(stderr, format, va);
57	if (err)
58		fprintf(stderr, ": %s", strerror(err));
59	putc('\n', stderr);
60}
61
62void error_msg(const char *format, ...)
63{
64	va_list va;
65
66	va_start(va, format);
67	do_error_msg(format, va, 0);
68	va_end(va);
69}
70
71void error_msg_errno(const char *format, ...)
72{
73	va_list va;
74
75	va_start(va, format);
76	do_error_msg(format, va, errno);
77	va_end(va);
78}
79
80__noreturn void fatal_error(const char *format, ...)
81{
82	va_list va;
83
84	va_start(va, format);
85	do_error_msg(format, va, 0);
86	va_end(va);
87	abort();
88}
89
90__noreturn void assertion_failed(const char *expr, const char *file, int line)
91{
92	fatal_error("Assertion failed: %s at %s:%d", expr, file, line);
93}
94
95static void print_libfsverity_error(const char *msg)
96{
97	error_msg("%s", msg);
98}
99
100void install_libfsverity_error_handler(void)
101{
102	libfsverity_set_error_callback(print_libfsverity_error);
103}
104
105/* ========== File utilities ========== */
106
107bool open_file(struct filedes *file, const char *filename, int flags, int mode)
108{
109	file->fd = open(filename, flags | O_BINARY, mode);
110	if (file->fd < 0) {
111		error_msg_errno("can't open '%s' for %s", filename,
112				(flags & O_ACCMODE) == O_RDONLY ? "reading" :
113				(flags & O_ACCMODE) == O_WRONLY ? "writing" :
114				"reading and writing");
115		return false;
116	}
117	file->name = xstrdup(filename);
118	return true;
119}
120
121bool get_file_size(struct filedes *file, u64 *size_ret)
122{
123	struct stat stbuf;
124
125	if (fstat(file->fd, &stbuf) != 0) {
126		error_msg_errno("can't stat file '%s'", file->name);
127		return false;
128	}
129	*size_ret = stbuf.st_size;
130	return true;
131}
132
133bool preallocate_file(struct filedes *file, u64 size)
134{
135	int res;
136
137	if (size == 0)
138		return true;
139#ifdef _WIN32
140	/* Not exactly the same as posix_fallocate(), but good enough... */
141	res = _chsize_s(file->fd, size);
142#else
143	res = posix_fallocate(file->fd, 0, size);
144#endif
145	if (res != 0) {
146		error_msg_errno("preallocating %" PRIu64 "-byte file '%s'",
147				size, file->name);
148		return false;
149	}
150	return true;
151}
152
153bool full_read(struct filedes *file, void *buf, size_t count)
154{
155	while (count) {
156		int n = read(file->fd, buf, min(count, INT_MAX));
157
158		if (n < 0) {
159			error_msg_errno("reading from '%s'", file->name);
160			return false;
161		}
162		if (n == 0) {
163			error_msg("unexpected end-of-file on '%s'", file->name);
164			return false;
165		}
166		buf += n;
167		count -= n;
168	}
169	return true;
170}
171
172bool full_write(struct filedes *file, const void *buf, size_t count)
173{
174	while (count) {
175		int n = write(file->fd, buf, min(count, INT_MAX));
176
177		if (n < 0) {
178			error_msg_errno("writing to '%s'", file->name);
179			return false;
180		}
181		buf += n;
182		count -= n;
183	}
184	return true;
185}
186
187static int raw_pwrite(int fd, const void *buf, int count, u64 offset)
188{
189#ifdef _WIN32
190	HANDLE h = (HANDLE)_get_osfhandle(fd);
191	OVERLAPPED pos = { .Offset = offset, .OffsetHigh = offset >> 32 };
192	DWORD written = 0;
193
194	/* Not exactly the same as pwrite(), but good enough... */
195	if (!WriteFile(h, buf, count, &written, &pos)) {
196		errno = EIO;
197		return -1;
198	}
199	return written;
200#else
201	return pwrite(fd, buf, count, offset);
202#endif
203}
204
205bool full_pwrite(struct filedes *file, const void *buf, size_t count,
206		 u64 offset)
207{
208	while (count) {
209		int n = raw_pwrite(file->fd, buf, min(count, INT_MAX), offset);
210
211		if (n < 0) {
212			error_msg_errno("writing to '%s'", file->name);
213			return false;
214		}
215		buf += n;
216		count -= n;
217		offset += n;
218	}
219	return true;
220}
221
222bool filedes_close(struct filedes *file)
223{
224	int res;
225
226	if (file->fd < 0)
227		return true;
228	res = close(file->fd);
229	if (res != 0)
230		error_msg_errno("closing '%s'", file->name);
231	file->fd = -1;
232	free(file->name);
233	file->name = NULL;
234	return res == 0;
235}
236
237int read_callback(void *file, void *buf, size_t count)
238{
239	errno = 0;
240	if (!full_read(file, buf, count))
241		return errno ? -errno : -EIO;
242	return 0;
243}
244
245/* ========== String utilities ========== */
246
247static int hex2bin_char(char c)
248{
249	if (c >= '0' && c <= '9')
250		return c - '0';
251	if (c >= 'a' && c <= 'f')
252		return 10 + (c - 'a');
253	if (c >= 'A' && c <= 'F')
254		return 10 + (c - 'A');
255	return -1;
256}
257
258bool hex2bin(const char *hex, u8 *bin, size_t bin_len)
259{
260	size_t i;
261
262	if (strlen(hex) != 2 * bin_len)
263		return false;
264
265	for (i = 0; i < bin_len; i++) {
266		int hi = hex2bin_char(*hex++);
267		int lo = hex2bin_char(*hex++);
268
269		if (hi < 0 || lo < 0)
270			return false;
271		bin[i] = (hi << 4) | lo;
272	}
273	return true;
274}
275
276static char bin2hex_char(u8 nibble)
277{
278	ASSERT(nibble <= 0xf);
279
280	if (nibble < 10)
281		return '0' + nibble;
282	return 'a' + (nibble - 10);
283}
284
285void bin2hex(const u8 *bin, size_t bin_len, char *hex)
286{
287	size_t i;
288
289	for (i = 0; i < bin_len; i++) {
290		*hex++ = bin2hex_char(bin[i] >> 4);
291		*hex++ = bin2hex_char(bin[i] & 0xf);
292	}
293	*hex = '\0';
294}
295