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