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
xmalloc(size_t size)27 void *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
xzalloc(size_t size)36 void *xzalloc(size_t size)
37 {
38 return memset(xmalloc(size), 0, size);
39 }
40
xmemdup(const void * mem,size_t size)41 void *xmemdup(const void *mem, size_t size)
42 {
43 return memcpy(xmalloc(size), mem, size);
44 }
45
xstrdup(const char * s)46 char *xstrdup(const char *s)
47 {
48 return xmemdup(s, strlen(s) + 1);
49 }
50
51 /* ========== Error messages and assertions ========== */
52
do_error_msg(const char * format,va_list va,int err)53 static 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
error_msg(const char * format,...)62 void 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
error_msg_errno(const char * format,...)71 void 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
fatal_error(const char * format,...)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
assertion_failed(const char * expr,const char * file,int line)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
print_libfsverity_error(const char * msg)95 static void print_libfsverity_error(const char *msg)
96 {
97 error_msg("%s", msg);
98 }
99
install_libfsverity_error_handler(void)100 void install_libfsverity_error_handler(void)
101 {
102 libfsverity_set_error_callback(print_libfsverity_error);
103 }
104
105 /* ========== File utilities ========== */
106
open_file(struct filedes * file,const char * filename,int flags,int mode)107 bool 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
get_file_size(struct filedes * file,u64 * size_ret)121 bool 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
preallocate_file(struct filedes * file,u64 size)133 bool 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
full_read(struct filedes * file,void * buf,size_t count)153 bool 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
full_write(struct filedes * file,const void * buf,size_t count)172 bool 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
raw_pwrite(int fd,const void * buf,int count,u64 offset)187 static 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
full_pwrite(struct filedes * file,const void * buf,size_t count,u64 offset)205 bool 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
filedes_close(struct filedes * file)222 bool 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
read_callback(void * file,void * buf,size_t count)237 int 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
hex2bin_char(char c)247 static 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
hex2bin(const char * hex,u8 * bin,size_t bin_len)258 bool 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
bin2hex_char(u8 nibble)276 static 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
bin2hex(const u8 * bin,size_t bin_len,char * hex)285 void 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