Home | History | Annotate | Download | only in fsverity-utils
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Utility functions for the 'fsverity' program
      4  *
      5  * Copyright (C) 2018 Google LLC
      6  *
      7  * Written by Eric Biggers.
      8  */
      9 
     10 #include <errno.h>
     11 #include <fcntl.h>
     12 #include <limits.h>
     13 #include <stdarg.h>
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #include <sys/stat.h>
     18 #include <unistd.h>
     19 
     20 #include "util.h"
     21 
     22 /* ========== Memory allocation ========== */
     23 
     24 void *xmalloc(size_t size)
     25 {
     26 	void *p = malloc(size);
     27 
     28 	if (!p)
     29 		fatal_error("out of memory");
     30 	return p;
     31 }
     32 
     33 void *xzalloc(size_t size)
     34 {
     35 	return memset(xmalloc(size), 0, size);
     36 }
     37 
     38 void *xmemdup(const void *mem, size_t size)
     39 {
     40 	return memcpy(xmalloc(size), mem, size);
     41 }
     42 
     43 char *xstrdup(const char *s)
     44 {
     45 	return xmemdup(s, strlen(s) + 1);
     46 }
     47 
     48 char *xasprintf(const char *format, ...)
     49 {
     50 	va_list va1, va2;
     51 	int size;
     52 	char *s;
     53 
     54 	va_start(va1, format);
     55 
     56 	va_copy(va2, va1);
     57 	size = vsnprintf(NULL, 0, format, va2);
     58 	va_end(va2);
     59 
     60 	ASSERT(size >= 0);
     61 	s = xmalloc(size + 1);
     62 	vsprintf(s, format, va1);
     63 
     64 	va_end(va1);
     65 	return s;
     66 }
     67 
     68 /* ========== Error messages and assertions ========== */
     69 
     70 void do_error_msg(const char *format, va_list va, int err)
     71 {
     72 	fputs("ERROR: ", stderr);
     73 	vfprintf(stderr, format, va);
     74 	if (err)
     75 		fprintf(stderr, ": %s", strerror(err));
     76 	putc('\n', stderr);
     77 }
     78 
     79 void error_msg(const char *format, ...)
     80 {
     81 	va_list va;
     82 
     83 	va_start(va, format);
     84 	do_error_msg(format, va, 0);
     85 	va_end(va);
     86 }
     87 
     88 void error_msg_errno(const char *format, ...)
     89 {
     90 	va_list va;
     91 
     92 	va_start(va, format);
     93 	do_error_msg(format, va, errno);
     94 	va_end(va);
     95 }
     96 
     97 __noreturn void fatal_error(const char *format, ...)
     98 {
     99 	va_list va;
    100 
    101 	va_start(va, format);
    102 	do_error_msg(format, va, 0);
    103 	va_end(va);
    104 	abort();
    105 }
    106 
    107 __noreturn void assertion_failed(const char *expr, const char *file, int line)
    108 {
    109 	fatal_error("Assertion failed: %s at %s:%d", expr, file, line);
    110 }
    111 
    112 /* ========== File utilities ========== */
    113 
    114 bool open_file(struct filedes *file, const char *filename, int flags, int mode)
    115 {
    116 	file->fd = open(filename, flags, mode);
    117 	if (file->fd < 0) {
    118 		error_msg_errno("can't open '%s' for %s", filename,
    119 				(flags & O_ACCMODE) == O_RDONLY ? "reading" :
    120 				(flags & O_ACCMODE) == O_WRONLY ? "writing" :
    121 				"reading and writing");
    122 		return false;
    123 	}
    124 	file->autodelete = false;
    125 	file->name = xstrdup(filename);
    126 	file->pos = 0;
    127 	return true;
    128 }
    129 
    130 bool open_tempfile(struct filedes *file)
    131 {
    132 	const char *tmpdir = getenv("TMPDIR") ?: P_tmpdir;
    133 	char *name = xasprintf("%s/fsverity-XXXXXX", tmpdir);
    134 
    135 	file->fd = mkstemp(name);
    136 	if (file->fd < 0) {
    137 		error_msg_errno("can't create temporary file");
    138 		free(name);
    139 		return false;
    140 	}
    141 	file->autodelete = true;
    142 	file->name = name;
    143 	file->pos = 0;
    144 	return true;
    145 }
    146 
    147 bool get_file_size(struct filedes *file, u64 *size_ret)
    148 {
    149 	struct stat stbuf;
    150 
    151 	if (fstat(file->fd, &stbuf) != 0) {
    152 		error_msg_errno("can't stat file '%s'", file->name);
    153 		return false;
    154 	}
    155 	*size_ret = stbuf.st_size;
    156 	return true;
    157 }
    158 
    159 bool filedes_seek(struct filedes *file, u64 pos, int whence)
    160 {
    161 	off_t res;
    162 
    163 	res = lseek(file->fd, pos, whence);
    164 	if (res < 0) {
    165 		error_msg_errno("seek error on '%s'", file->name);
    166 		return false;
    167 	}
    168 	file->pos = res;
    169 	return true;
    170 }
    171 
    172 bool full_read(struct filedes *file, void *buf, size_t count)
    173 {
    174 	while (count) {
    175 		int n = read(file->fd, buf, min(count, INT_MAX));
    176 
    177 		if (n < 0) {
    178 			error_msg_errno("reading from '%s'", file->name);
    179 			return false;
    180 		}
    181 		if (n == 0) {
    182 			error_msg("unexpected end-of-file on '%s'", file->name);
    183 			return false;
    184 		}
    185 		buf += n;
    186 		count -= n;
    187 		file->pos += n;
    188 	}
    189 	return true;
    190 }
    191 
    192 bool full_pread(struct filedes *file, void *buf, size_t count, u64 offset)
    193 {
    194 	while (count) {
    195 		int n = pread(file->fd, buf, min(count, INT_MAX), offset);
    196 
    197 		if (n < 0) {
    198 			error_msg_errno("reading from '%s'", file->name);
    199 			return false;
    200 		}
    201 		if (n == 0) {
    202 			error_msg("unexpected end-of-file on '%s'", file->name);
    203 			return false;
    204 		}
    205 		buf += n;
    206 		count -= n;
    207 		offset += n;
    208 	}
    209 	return true;
    210 }
    211 
    212 bool full_write(struct filedes *file, const void *buf, size_t count)
    213 {
    214 	while (count) {
    215 		int n = write(file->fd, buf, min(count, INT_MAX));
    216 
    217 		if (n < 0) {
    218 			error_msg_errno("writing to '%s'", file->name);
    219 			return false;
    220 		}
    221 		buf += n;
    222 		count -= n;
    223 		file->pos += n;
    224 	}
    225 	return true;
    226 }
    227 
    228 bool full_pwrite(struct filedes *file, const void *buf, size_t count,
    229 		 u64 offset)
    230 {
    231 	while (count) {
    232 		int n = pwrite(file->fd, buf, min(count, INT_MAX), offset);
    233 
    234 		if (n < 0) {
    235 			error_msg_errno("writing to '%s'", file->name);
    236 			return false;
    237 		}
    238 		buf += n;
    239 		count -= n;
    240 		offset += n;
    241 	}
    242 	return true;
    243 }
    244 
    245 /* Copy 'count' bytes of data from 'src' to 'dst' */
    246 bool copy_file_data(struct filedes *src, struct filedes *dst, u64 count)
    247 {
    248 	char buf[4096];
    249 
    250 	while (count) {
    251 		size_t n = min(count, sizeof(buf));
    252 
    253 		if (!full_read(src, buf, n))
    254 			return false;
    255 		if (!full_write(dst, buf, n))
    256 			return false;
    257 		count -= n;
    258 	}
    259 	return true;
    260 }
    261 
    262 /* Write 'count' bytes of zeroes to the file */
    263 bool write_zeroes(struct filedes *file, u64 count)
    264 {
    265 	char buf[4096];
    266 
    267 	memset(buf, 0, min(count, sizeof(buf)));
    268 
    269 	while (count) {
    270 		size_t n = min(count, sizeof(buf));
    271 
    272 		if (!full_write(file, buf, n))
    273 			return false;
    274 		count -= n;
    275 	}
    276 	return true;
    277 }
    278 
    279 bool filedes_close(struct filedes *file)
    280 {
    281 	int res;
    282 
    283 	if (file->fd < 0)
    284 		return true;
    285 	res = close(file->fd);
    286 	if (res != 0)
    287 		error_msg_errno("closing '%s'", file->name);
    288 	if (file->autodelete)
    289 		(void)unlink(file->name);
    290 	file->fd = -1;
    291 	free(file->name);
    292 	file->name = NULL;
    293 	return res == 0;
    294 }
    295 
    296 /* ========== String utilities ========== */
    297 
    298 static int hex2bin_char(char c)
    299 {
    300 	if (c >= '0' && c <= '9')
    301 		return c - '0';
    302 	if (c >= 'a' && c <= 'f')
    303 		return 10 + (c - 'a');
    304 	if (c >= 'A' && c <= 'F')
    305 		return 10 + (c - 'A');
    306 	return -1;
    307 }
    308 
    309 bool hex2bin(const char *hex, u8 *bin, size_t bin_len)
    310 {
    311 	if (strlen(hex) != 2 * bin_len)
    312 		return false;
    313 
    314 	while (bin_len--) {
    315 		int hi = hex2bin_char(*hex++);
    316 		int lo = hex2bin_char(*hex++);
    317 
    318 		if (hi < 0 || lo < 0)
    319 			return false;
    320 		*bin++ = (hi << 4) | lo;
    321 	}
    322 	return true;
    323 }
    324 
    325 static char bin2hex_char(u8 nibble)
    326 {
    327 	ASSERT(nibble <= 0xf);
    328 
    329 	if (nibble < 10)
    330 		return '0' + nibble;
    331 	return 'a' + (nibble - 10);
    332 }
    333 
    334 void bin2hex(const u8 *bin, size_t bin_len, char *hex)
    335 {
    336 	while (bin_len--) {
    337 		*hex++ = bin2hex_char(*bin >> 4);
    338 		*hex++ = bin2hex_char(*bin & 0xf);
    339 		bin++;
    340 	}
    341 	*hex = '\0';
    342 }
    343 
    344 void string_list_append(struct string_list *list, char *string)
    345 {
    346 	ASSERT(list->length <= list->capacity);
    347 	if (list->length == list->capacity) {
    348 		list->capacity = (list->capacity * 2) + 4;
    349 		list->strings = realloc(list->strings,
    350 					sizeof(list->strings[0]) *
    351 					list->capacity);
    352 		if (!list->strings)
    353 			fatal_error("out of memory");
    354 	}
    355 	list->strings[list->length++] = string;
    356 }
    357 
    358 void string_list_destroy(struct string_list *list)
    359 {
    360 	free(list->strings);
    361 	memset(list, 0, sizeof(*list));
    362 }
    363