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