1 /*
2 * Copyright (c) 2019 SUSE Software Solutions Germany GmbH
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation;
7 * version 2.1 of the License.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #define __XEN_TOOLS__ 1
19
20 #define _GNU_SOURCE
21
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <zlib.h>
28
29 #include <xentoollog.h>
30 #include <xenhypfs.h>
31 #include <xencall.h>
32 #include <xentoolcore_internal.h>
33
34 #include <xen/xen.h>
35 #include <xen/hypfs.h>
36
37 #define BUF_SIZE 4096
38
39 struct xenhypfs_handle {
40 xentoollog_logger *logger, *logger_tofree;
41 unsigned int flags;
42 xencall_handle *xcall;
43 };
44
xenhypfs_open(xentoollog_logger * logger,unsigned open_flags)45 xenhypfs_handle *xenhypfs_open(xentoollog_logger *logger,
46 unsigned open_flags)
47 {
48 xenhypfs_handle *fshdl = calloc(1, sizeof(*fshdl));
49
50 if (!fshdl)
51 return NULL;
52
53 fshdl->flags = open_flags;
54 fshdl->logger = logger;
55 fshdl->logger_tofree = NULL;
56
57 if (!fshdl->logger) {
58 fshdl->logger = fshdl->logger_tofree =
59 (xentoollog_logger*)
60 xtl_createlogger_stdiostream(stderr, XTL_PROGRESS, 0);
61 if (!fshdl->logger)
62 goto err;
63 }
64
65 fshdl->xcall = xencall_open(fshdl->logger, 0);
66 if (!fshdl->xcall)
67 goto err;
68
69 /* No need to remember supported version, we only support V1. */
70 if (xencall5(fshdl->xcall, __HYPERVISOR_hypfs_op,
71 XEN_HYPFS_OP_get_version, 0, 0, 0, 0) < 0)
72 goto err;
73
74 return fshdl;
75
76 err:
77 xencall_close(fshdl->xcall);
78 xtl_logger_destroy(fshdl->logger_tofree);
79 free(fshdl);
80 return NULL;
81 }
82
xenhypfs_close(xenhypfs_handle * fshdl)83 int xenhypfs_close(xenhypfs_handle *fshdl)
84 {
85 if (!fshdl)
86 return 0;
87
88 xencall_close(fshdl->xcall);
89 xtl_logger_destroy(fshdl->logger_tofree);
90 free(fshdl);
91 return 0;
92 }
93
xenhypfs_get_pathbuf(xenhypfs_handle * fshdl,const char * path,char ** path_buf)94 static int xenhypfs_get_pathbuf(xenhypfs_handle *fshdl, const char *path,
95 char **path_buf)
96 {
97 int ret = -1;
98 int path_sz;
99
100 if (!fshdl) {
101 errno = EBADF;
102 goto out;
103 }
104
105 path_sz = strlen(path) + 1;
106 if (path_sz > XEN_HYPFS_MAX_PATHLEN)
107 {
108 errno = ENAMETOOLONG;
109 goto out;
110 }
111
112 *path_buf = xencall_alloc_buffer(fshdl->xcall, path_sz);
113 if (!*path_buf) {
114 errno = ENOMEM;
115 goto out;
116 }
117 strcpy(*path_buf, path);
118
119 ret = path_sz;
120
121 out:
122 return ret;
123 }
124
xenhypfs_inflate(void * in_data,size_t * sz)125 static void *xenhypfs_inflate(void *in_data, size_t *sz)
126 {
127 unsigned char *workbuf;
128 void *content = NULL;
129 unsigned int out_sz;
130 z_stream z = { .opaque = NULL };
131 int ret;
132
133 workbuf = malloc(BUF_SIZE);
134 if (!workbuf)
135 return NULL;
136
137 z.next_in = in_data;
138 z.avail_in = *sz;
139 ret = inflateInit2(&z, MAX_WBITS + 32); /* 32 == gzip */
140
141 for (*sz = 0; ret == Z_OK; *sz += out_sz) {
142 z.next_out = workbuf;
143 z.avail_out = BUF_SIZE;
144 ret = inflate(&z, Z_SYNC_FLUSH);
145 if (ret != Z_OK && ret != Z_STREAM_END)
146 break;
147
148 out_sz = z.next_out - workbuf;
149 content = realloc(content, *sz + out_sz + 1);
150 if (!content) {
151 ret = Z_MEM_ERROR;
152 break;
153 }
154 memcpy(content + *sz, workbuf, out_sz);
155 *(char *)(content + *sz + out_sz) = 0;
156 }
157
158 inflateEnd(&z);
159 if (ret != Z_STREAM_END) {
160 free(content);
161 content = NULL;
162 errno = EIO;
163 }
164 free(workbuf);
165 return content;
166 }
167
xenhypfs_set_attrs(struct xen_hypfs_direntry * entry,struct xenhypfs_dirent * dirent)168 static void xenhypfs_set_attrs(struct xen_hypfs_direntry *entry,
169 struct xenhypfs_dirent *dirent)
170 {
171 dirent->size = entry->content_len;
172
173 switch(entry->type) {
174 case XEN_HYPFS_TYPE_DIR:
175 dirent->type = xenhypfs_type_dir;
176 break;
177 case XEN_HYPFS_TYPE_BLOB:
178 dirent->type = xenhypfs_type_blob;
179 break;
180 case XEN_HYPFS_TYPE_STRING:
181 dirent->type = xenhypfs_type_string;
182 break;
183 case XEN_HYPFS_TYPE_UINT:
184 dirent->type = xenhypfs_type_uint;
185 break;
186 case XEN_HYPFS_TYPE_INT:
187 dirent->type = xenhypfs_type_int;
188 break;
189 case XEN_HYPFS_TYPE_BOOL:
190 dirent->type = xenhypfs_type_bool;
191 break;
192 default:
193 dirent->type = xenhypfs_type_blob;
194 }
195
196 switch (entry->encoding) {
197 case XEN_HYPFS_ENC_PLAIN:
198 dirent->encoding = xenhypfs_enc_plain;
199 break;
200 case XEN_HYPFS_ENC_GZIP:
201 dirent->encoding = xenhypfs_enc_gzip;
202 break;
203 default:
204 dirent->encoding = xenhypfs_enc_plain;
205 dirent->type = xenhypfs_type_blob;
206 }
207
208 dirent->flags = entry->max_write_len ? XENHYPFS_FLAG_WRITABLE : 0;
209 }
210
xenhypfs_read_raw(xenhypfs_handle * fshdl,const char * path,struct xenhypfs_dirent ** dirent)211 void *xenhypfs_read_raw(xenhypfs_handle *fshdl, const char *path,
212 struct xenhypfs_dirent **dirent)
213 {
214 void *retbuf = NULL, *content = NULL;
215 char *path_buf = NULL;
216 const char *name;
217 struct xen_hypfs_direntry *entry;
218 int ret;
219 int sz, path_sz;
220
221 *dirent = NULL;
222 ret = xenhypfs_get_pathbuf(fshdl, path, &path_buf);
223 if (ret < 0)
224 goto out;
225
226 path_sz = ret;
227
228 for (sz = BUF_SIZE;; sz = sizeof(*entry) + entry->content_len) {
229 if (retbuf)
230 xencall_free_buffer(fshdl->xcall, retbuf);
231
232 retbuf = xencall_alloc_buffer(fshdl->xcall, sz);
233 if (!retbuf) {
234 errno = ENOMEM;
235 goto out;
236 }
237 entry = retbuf;
238
239 ret = xencall5(fshdl->xcall, __HYPERVISOR_hypfs_op, XEN_HYPFS_OP_read,
240 (unsigned long)path_buf, path_sz,
241 (unsigned long)retbuf, sz);
242 if (!ret)
243 break;
244
245 if (errno != ENOBUFS)
246 goto out;
247 }
248
249 content = malloc(entry->content_len);
250 if (!content)
251 goto out;
252 memcpy(content, entry + 1, entry->content_len);
253
254 name = strrchr(path, '/');
255 if (!name)
256 name = path;
257 else {
258 name++;
259 if (!*name)
260 name--;
261 }
262 *dirent = calloc(1, sizeof(struct xenhypfs_dirent) + strlen(name) + 1);
263 if (!*dirent) {
264 free(content);
265 content = NULL;
266 errno = ENOMEM;
267 goto out;
268 }
269 (*dirent)->name = (char *)(*dirent + 1);
270 strcpy((*dirent)->name, name);
271 xenhypfs_set_attrs(entry, *dirent);
272
273 out:
274 ret = errno;
275 xencall_free_buffer(fshdl->xcall, path_buf);
276 xencall_free_buffer(fshdl->xcall, retbuf);
277 errno = ret;
278
279 return content;
280 }
281
xenhypfs_read(xenhypfs_handle * fshdl,const char * path)282 char *xenhypfs_read(xenhypfs_handle *fshdl, const char *path)
283 {
284 char *buf, *ret_buf = NULL;
285 struct xenhypfs_dirent *dirent;
286 int ret;
287
288 buf = xenhypfs_read_raw(fshdl, path, &dirent);
289 if (!buf)
290 goto out;
291
292 switch (dirent->encoding) {
293 case xenhypfs_enc_plain:
294 break;
295 case xenhypfs_enc_gzip:
296 ret_buf = xenhypfs_inflate(buf, &dirent->size);
297 if (!ret_buf)
298 goto out;
299 free(buf);
300 buf = ret_buf;
301 ret_buf = NULL;
302 break;
303 }
304
305 switch (dirent->type) {
306 case xenhypfs_type_dir:
307 errno = EISDIR;
308 break;
309 case xenhypfs_type_blob:
310 errno = EDOM;
311 break;
312 case xenhypfs_type_string:
313 ret_buf = buf;
314 buf = NULL;
315 break;
316 case xenhypfs_type_uint:
317 case xenhypfs_type_bool:
318 switch (dirent->size) {
319 case 1:
320 ret = asprintf(&ret_buf, "%"PRIu8, *(uint8_t *)buf);
321 break;
322 case 2:
323 ret = asprintf(&ret_buf, "%"PRIu16, *(uint16_t *)buf);
324 break;
325 case 4:
326 ret = asprintf(&ret_buf, "%"PRIu32, *(uint32_t *)buf);
327 break;
328 case 8:
329 ret = asprintf(&ret_buf, "%"PRIu64, *(uint64_t *)buf);
330 break;
331 default:
332 ret = -1;
333 errno = EDOM;
334 }
335 if (ret < 0)
336 ret_buf = NULL;
337 break;
338 case xenhypfs_type_int:
339 switch (dirent->size) {
340 case 1:
341 ret = asprintf(&ret_buf, "%"PRId8, *(int8_t *)buf);
342 break;
343 case 2:
344 ret = asprintf(&ret_buf, "%"PRId16, *(int16_t *)buf);
345 break;
346 case 4:
347 ret = asprintf(&ret_buf, "%"PRId32, *(int32_t *)buf);
348 break;
349 case 8:
350 ret = asprintf(&ret_buf, "%"PRId64, *(int64_t *)buf);
351 break;
352 default:
353 ret = -1;
354 errno = EDOM;
355 }
356 if (ret < 0)
357 ret_buf = NULL;
358 break;
359 }
360
361 out:
362 ret = errno;
363 free(buf);
364 free(dirent);
365 errno = ret;
366
367 return ret_buf;
368 }
369
xenhypfs_readdir(xenhypfs_handle * fshdl,const char * path,unsigned int * num_entries)370 struct xenhypfs_dirent *xenhypfs_readdir(xenhypfs_handle *fshdl,
371 const char *path,
372 unsigned int *num_entries)
373 {
374 void *buf, *curr;
375 int ret;
376 char *names;
377 struct xenhypfs_dirent *ret_buf = NULL, *dirent;
378 unsigned int n = 0, name_sz = 0;
379 struct xen_hypfs_dirlistentry *entry;
380
381 buf = xenhypfs_read_raw(fshdl, path, &dirent);
382 if (!buf)
383 goto out;
384
385 if (dirent->type != xenhypfs_type_dir ||
386 dirent->encoding != xenhypfs_enc_plain) {
387 errno = ENOTDIR;
388 goto out;
389 }
390
391 if (dirent->size) {
392 curr = buf;
393 for (n = 1;; n++) {
394 entry = curr;
395 name_sz += strlen(entry->name) + 1;
396 if (!entry->off_next)
397 break;
398
399 curr += entry->off_next;
400 }
401 }
402
403 ret_buf = malloc(n * sizeof(*ret_buf) + name_sz);
404 if (!ret_buf)
405 goto out;
406
407 *num_entries = n;
408 names = (char *)(ret_buf + n);
409 curr = buf;
410 for (n = 0; n < *num_entries; n++) {
411 entry = curr;
412 xenhypfs_set_attrs(&entry->e, ret_buf + n);
413 ret_buf[n].name = names;
414 strcpy(names, entry->name);
415 names += strlen(entry->name) + 1;
416 curr += entry->off_next;
417 }
418
419 out:
420 ret = errno;
421 free(buf);
422 free(dirent);
423 errno = ret;
424
425 return ret_buf;
426 }
427
xenhypfs_write(xenhypfs_handle * fshdl,const char * path,const char * val)428 int xenhypfs_write(xenhypfs_handle *fshdl, const char *path, const char *val)
429 {
430 void *buf = NULL;
431 char *path_buf = NULL, *val_end;
432 int ret, saved_errno;
433 int sz, path_sz;
434 struct xen_hypfs_direntry *entry;
435 uint64_t mask;
436
437 ret = xenhypfs_get_pathbuf(fshdl, path, &path_buf);
438 if (ret < 0)
439 goto out;
440
441 path_sz = ret;
442 ret = -1;
443
444 sz = BUF_SIZE;
445 buf = xencall_alloc_buffer(fshdl->xcall, sz);
446 if (!buf) {
447 errno = ENOMEM;
448 goto out;
449 }
450
451 ret = xencall5(fshdl->xcall, __HYPERVISOR_hypfs_op, XEN_HYPFS_OP_read,
452 (unsigned long)path_buf, path_sz,
453 (unsigned long)buf, sizeof(*entry));
454 if (ret && errno != ENOBUFS)
455 goto out;
456 ret = -1;
457 entry = buf;
458 if (!entry->max_write_len) {
459 errno = EACCES;
460 goto out;
461 }
462 if (entry->encoding != XEN_HYPFS_ENC_PLAIN) {
463 /* Writing compressed data currently not supported. */
464 errno = EDOM;
465 goto out;
466 }
467
468 switch (entry->type) {
469 case XEN_HYPFS_TYPE_STRING:
470 if (sz < strlen(val) + 1) {
471 sz = strlen(val) + 1;
472 xencall_free_buffer(fshdl->xcall, buf);
473 buf = xencall_alloc_buffer(fshdl->xcall, sz);
474 if (!buf) {
475 errno = ENOMEM;
476 goto out;
477 }
478 }
479 sz = strlen(val) + 1;
480 strcpy(buf, val);
481 break;
482 case XEN_HYPFS_TYPE_UINT:
483 sz = entry->content_len;
484 errno = 0;
485 *(unsigned long long *)buf = strtoull(val, &val_end, 0);
486 if (errno || !*val || *val_end)
487 goto out;
488 mask = ~0ULL << (8 * sz);
489 if ((*(uint64_t *)buf & mask) && ((*(uint64_t *)buf & mask) != mask)) {
490 errno = ERANGE;
491 goto out;
492 }
493 break;
494 case XEN_HYPFS_TYPE_INT:
495 sz = entry->content_len;
496 errno = 0;
497 *(unsigned long long *)buf = strtoll(val, &val_end, 0);
498 if (errno || !*val || *val_end)
499 goto out;
500 mask = (sz == 8) ? 0 : ~0ULL << (8 * sz);
501 if ((*(uint64_t *)buf & mask) && ((*(uint64_t *)buf & mask) != mask)) {
502 errno = ERANGE;
503 goto out;
504 }
505 break;
506 case XEN_HYPFS_TYPE_BOOL:
507 sz = entry->content_len;
508 *(unsigned long long *)buf = 0;
509 if (!strcmp(val, "1") || !strcmp(val, "on") || !strcmp(val, "yes") ||
510 !strcmp(val, "true") || !strcmp(val, "enable"))
511 *(unsigned long long *)buf = 1;
512 else if (strcmp(val, "0") && strcmp(val, "no") && strcmp(val, "off") &&
513 strcmp(val, "false") && strcmp(val, "disable")) {
514 errno = EDOM;
515 goto out;
516 }
517 break;
518 default:
519 /* No support for other types (yet). */
520 errno = EDOM;
521 goto out;
522 }
523
524 ret = xencall5(fshdl->xcall, __HYPERVISOR_hypfs_op,
525 XEN_HYPFS_OP_write_contents,
526 (unsigned long)path_buf, path_sz,
527 (unsigned long)buf, sz);
528
529 out:
530 saved_errno = errno;
531 xencall_free_buffer(fshdl->xcall, path_buf);
532 xencall_free_buffer(fshdl->xcall, buf);
533 errno = saved_errno;
534 return ret;
535 }
536