• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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