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