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
xmalloc(size_t size)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
xzalloc(size_t size)33 void *xzalloc(size_t size)
34 {
35 return memset(xmalloc(size), 0, size);
36 }
37
xmemdup(const void * mem,size_t size)38 void *xmemdup(const void *mem, size_t size)
39 {
40 return memcpy(xmalloc(size), mem, size);
41 }
42
xstrdup(const char * s)43 char *xstrdup(const char *s)
44 {
45 return xmemdup(s, strlen(s) + 1);
46 }
47
xasprintf(const char * format,...)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
do_error_msg(const char * format,va_list va,int err)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
error_msg(const char * format,...)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
error_msg_errno(const char * format,...)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
fatal_error(const char * format,...)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
assertion_failed(const char * expr,const char * file,int line)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
open_file(struct filedes * file,const char * filename,int flags,int mode)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
open_tempfile(struct filedes * file)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
get_file_size(struct filedes * file,u64 * size_ret)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
filedes_seek(struct filedes * file,u64 pos,int whence)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
full_read(struct filedes * file,void * buf,size_t count)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
full_pread(struct filedes * file,void * buf,size_t count,u64 offset)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
full_write(struct filedes * file,const void * buf,size_t count)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
full_pwrite(struct filedes * file,const void * buf,size_t count,u64 offset)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' */
copy_file_data(struct filedes * src,struct filedes * dst,u64 count)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 */
write_zeroes(struct filedes * file,u64 count)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
filedes_close(struct filedes * file)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
hex2bin_char(char c)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
hex2bin(const char * hex,u8 * bin,size_t bin_len)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
bin2hex_char(u8 nibble)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
bin2hex(const u8 * bin,size_t bin_len,char * hex)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
string_list_append(struct string_list * list,char * string)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
string_list_destroy(struct string_list * list)358 void string_list_destroy(struct string_list *list)
359 {
360 free(list->strings);
361 memset(list, 0, sizeof(*list));
362 }
363