// SPDX-License-Identifier: GPL-2.0+ /* * Utility functions for the 'fsverity' program * * Copyright (C) 2018 Google LLC * * Written by Eric Biggers. */ #include <errno.h> #include <fcntl.h> #include <limits.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include "util.h" /* ========== Memory allocation ========== */ void *xmalloc(size_t size) { void *p = malloc(size); if (!p) fatal_error("out of memory"); return p; } void *xzalloc(size_t size) { return memset(xmalloc(size), 0, size); } void *xmemdup(const void *mem, size_t size) { return memcpy(xmalloc(size), mem, size); } char *xstrdup(const char *s) { return xmemdup(s, strlen(s) + 1); } char *xasprintf(const char *format, ...) { va_list va1, va2; int size; char *s; va_start(va1, format); va_copy(va2, va1); size = vsnprintf(NULL, 0, format, va2); va_end(va2); ASSERT(size >= 0); s = xmalloc(size + 1); vsprintf(s, format, va1); va_end(va1); return s; } /* ========== Error messages and assertions ========== */ void do_error_msg(const char *format, va_list va, int err) { fputs("ERROR: ", stderr); vfprintf(stderr, format, va); if (err) fprintf(stderr, ": %s", strerror(err)); putc('\n', stderr); } void error_msg(const char *format, ...) { va_list va; va_start(va, format); do_error_msg(format, va, 0); va_end(va); } void error_msg_errno(const char *format, ...) { va_list va; va_start(va, format); do_error_msg(format, va, errno); va_end(va); } __noreturn void fatal_error(const char *format, ...) { va_list va; va_start(va, format); do_error_msg(format, va, 0); va_end(va); abort(); } __noreturn void assertion_failed(const char *expr, const char *file, int line) { fatal_error("Assertion failed: %s at %s:%d", expr, file, line); } /* ========== File utilities ========== */ bool open_file(struct filedes *file, const char *filename, int flags, int mode) { file->fd = open(filename, flags, mode); if (file->fd < 0) { error_msg_errno("can't open '%s' for %s", filename, (flags & O_ACCMODE) == O_RDONLY ? "reading" : (flags & O_ACCMODE) == O_WRONLY ? "writing" : "reading and writing"); return false; } file->autodelete = false; file->name = xstrdup(filename); file->pos = 0; return true; } bool open_tempfile(struct filedes *file) { const char *tmpdir = getenv("TMPDIR") ?: P_tmpdir; char *name = xasprintf("%s/fsverity-XXXXXX", tmpdir); file->fd = mkstemp(name); if (file->fd < 0) { error_msg_errno("can't create temporary file"); free(name); return false; } file->autodelete = true; file->name = name; file->pos = 0; return true; } bool get_file_size(struct filedes *file, u64 *size_ret) { struct stat stbuf; if (fstat(file->fd, &stbuf) != 0) { error_msg_errno("can't stat file '%s'", file->name); return false; } *size_ret = stbuf.st_size; return true; } bool filedes_seek(struct filedes *file, u64 pos, int whence) { off_t res; res = lseek(file->fd, pos, whence); if (res < 0) { error_msg_errno("seek error on '%s'", file->name); return false; } file->pos = res; return true; } bool full_read(struct filedes *file, void *buf, size_t count) { while (count) { int n = read(file->fd, buf, min(count, INT_MAX)); if (n < 0) { error_msg_errno("reading from '%s'", file->name); return false; } if (n == 0) { error_msg("unexpected end-of-file on '%s'", file->name); return false; } buf += n; count -= n; file->pos += n; } return true; } bool full_pread(struct filedes *file, void *buf, size_t count, u64 offset) { while (count) { int n = pread(file->fd, buf, min(count, INT_MAX), offset); if (n < 0) { error_msg_errno("reading from '%s'", file->name); return false; } if (n == 0) { error_msg("unexpected end-of-file on '%s'", file->name); return false; } buf += n; count -= n; offset += n; } return true; } bool full_write(struct filedes *file, const void *buf, size_t count) { while (count) { int n = write(file->fd, buf, min(count, INT_MAX)); if (n < 0) { error_msg_errno("writing to '%s'", file->name); return false; } buf += n; count -= n; file->pos += n; } return true; } bool full_pwrite(struct filedes *file, const void *buf, size_t count, u64 offset) { while (count) { int n = pwrite(file->fd, buf, min(count, INT_MAX), offset); if (n < 0) { error_msg_errno("writing to '%s'", file->name); return false; } buf += n; count -= n; offset += n; } return true; } /* Copy 'count' bytes of data from 'src' to 'dst' */ bool copy_file_data(struct filedes *src, struct filedes *dst, u64 count) { char buf[4096]; while (count) { size_t n = min(count, sizeof(buf)); if (!full_read(src, buf, n)) return false; if (!full_write(dst, buf, n)) return false; count -= n; } return true; } /* Write 'count' bytes of zeroes to the file */ bool write_zeroes(struct filedes *file, u64 count) { char buf[4096]; memset(buf, 0, min(count, sizeof(buf))); while (count) { size_t n = min(count, sizeof(buf)); if (!full_write(file, buf, n)) return false; count -= n; } return true; } bool filedes_close(struct filedes *file) { int res; if (file->fd < 0) return true; res = close(file->fd); if (res != 0) error_msg_errno("closing '%s'", file->name); if (file->autodelete) (void)unlink(file->name); file->fd = -1; free(file->name); file->name = NULL; return res == 0; } /* ========== String utilities ========== */ static int hex2bin_char(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); return -1; } bool hex2bin(const char *hex, u8 *bin, size_t bin_len) { if (strlen(hex) != 2 * bin_len) return false; while (bin_len--) { int hi = hex2bin_char(*hex++); int lo = hex2bin_char(*hex++); if (hi < 0 || lo < 0) return false; *bin++ = (hi << 4) | lo; } return true; } static char bin2hex_char(u8 nibble) { ASSERT(nibble <= 0xf); if (nibble < 10) return '0' + nibble; return 'a' + (nibble - 10); } void bin2hex(const u8 *bin, size_t bin_len, char *hex) { while (bin_len--) { *hex++ = bin2hex_char(*bin >> 4); *hex++ = bin2hex_char(*bin & 0xf); bin++; } *hex = '\0'; } void string_list_append(struct string_list *list, char *string) { ASSERT(list->length <= list->capacity); if (list->length == list->capacity) { list->capacity = (list->capacity * 2) + 4; list->strings = realloc(list->strings, sizeof(list->strings[0]) * list->capacity); if (!list->strings) fatal_error("out of memory"); } list->strings[list->length++] = string; } void string_list_destroy(struct string_list *list) { free(list->strings); memset(list, 0, sizeof(*list)); }