1/*
2 * Copyright (c) Christos Zoulas 2003.
3 * All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice immediately at the beginning of the file, without modification,
10 *    this list of conditions, and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27#include "file.h"
28
29#ifndef	lint
30FILE_RCSID("@(#)$File: funcs.c,v 1.115 2020/02/20 15:50:20 christos Exp $")
31#endif	/* lint */
32
33#include "magic.h"
34#include <assert.h>
35#include <stdarg.h>
36#include <stdlib.h>
37#include <string.h>
38#include <ctype.h>
39#if defined(HAVE_WCHAR_H)
40#include <wchar.h>
41#endif
42#if defined(HAVE_WCTYPE_H)
43#include <wctype.h>
44#endif
45#include <limits.h>
46
47#ifndef SIZE_MAX
48#define SIZE_MAX	((size_t)~0)
49#endif
50
51protected char *
52file_copystr(char *buf, size_t blen, size_t width, const char *str)
53{
54	if (++width > blen)
55		width = blen;
56	strlcpy(buf, str, width);
57	return buf;
58}
59
60private void
61file_clearbuf(struct magic_set *ms)
62{
63	free(ms->o.buf);
64	ms->o.buf = NULL;
65	ms->o.blen = 0;
66}
67
68private int
69file_checkfield(char *msg, size_t mlen, const char *what, const char **pp)
70{
71	const char *p = *pp;
72	int fw = 0;
73
74	while (*p && isdigit((unsigned char)*p))
75		fw = fw * 10 + (*p++ - '0');
76
77	*pp = p;
78
79	if (fw < 1024)
80		return 1;
81	if (msg)
82		snprintf(msg, mlen, "field %s too large: %d", what, fw);
83
84	return 0;
85}
86
87protected int
88file_checkfmt(char *msg, size_t mlen, const char *fmt)
89{
90	for (const char *p = fmt; *p; p++) {
91		if (*p != '%')
92			continue;
93		if (*++p == '%')
94			continue;
95		// Skip uninteresting.
96		while (strchr("0.'+- ", *p) != NULL)
97			p++;
98		if (*p == '*') {
99			if (msg)
100				snprintf(msg, mlen, "* not allowed in format");
101			return -1;
102		}
103
104		if (!file_checkfield(msg, mlen, "width", &p))
105			return -1;
106
107		if (*p == '.') {
108			p++;
109			if (!file_checkfield(msg, mlen, "precision", &p))
110				return -1;
111		}
112
113		if (!isalpha((unsigned char)*p)) {
114			if (msg)
115				snprintf(msg, mlen, "bad format char: %c", *p);
116			return -1;
117		}
118	}
119	return 0;
120}
121
122/*
123 * Like printf, only we append to a buffer.
124 */
125protected int
126file_vprintf(struct magic_set *ms, const char *fmt, va_list ap)
127{
128	int len;
129	char *buf, *newstr;
130	char tbuf[1024];
131
132	if (ms->event_flags & EVENT_HAD_ERR)
133		return 0;
134
135	if (file_checkfmt(tbuf, sizeof(tbuf), fmt)) {
136		file_clearbuf(ms);
137		file_error(ms, 0, "Bad magic format `%s' (%s)", fmt, tbuf);
138		return -1;
139	}
140
141	len = vasprintf(&buf, fmt, ap);
142	if (len < 0 || (size_t)len > 1024 || len + ms->o.blen > 1024 * 1024) {
143		size_t blen = ms->o.blen;
144		free(buf);
145		file_clearbuf(ms);
146		file_error(ms, 0, "Output buffer space exceeded %d+%zu", len,
147		    blen);
148		return -1;
149	}
150
151	if (ms->o.buf != NULL) {
152		len = asprintf(&newstr, "%s%s", ms->o.buf, buf);
153		free(buf);
154		if (len < 0)
155			goto out;
156		free(ms->o.buf);
157		buf = newstr;
158	}
159	ms->o.buf = buf;
160	ms->o.blen = len;
161	return 0;
162out:
163	file_clearbuf(ms);
164	file_error(ms, errno, "vasprintf failed");
165	return -1;
166}
167
168protected int
169file_printf(struct magic_set *ms, const char *fmt, ...)
170{
171	int rv;
172	va_list ap;
173
174	va_start(ap, fmt);
175	rv = file_vprintf(ms, fmt, ap);
176	va_end(ap);
177	return rv;
178}
179
180/*
181 * error - print best error message possible
182 */
183/*VARARGS*/
184__attribute__((__format__(__printf__, 3, 0)))
185private void
186file_error_core(struct magic_set *ms, int error, const char *f, va_list va,
187    size_t lineno)
188{
189	/* Only the first error is ok */
190	if (ms->event_flags & EVENT_HAD_ERR)
191		return;
192	if (lineno != 0) {
193		file_clearbuf(ms);
194		(void)file_printf(ms, "line %" SIZE_T_FORMAT "u:", lineno);
195	}
196	if (ms->o.buf && *ms->o.buf)
197		(void)file_printf(ms, " ");
198	(void)file_vprintf(ms, f, va);
199	if (error > 0)
200		(void)file_printf(ms, " (%s)", strerror(error));
201	ms->event_flags |= EVENT_HAD_ERR;
202	ms->error = error;
203}
204
205/*VARARGS*/
206protected void
207file_error(struct magic_set *ms, int error, const char *f, ...)
208{
209	va_list va;
210	va_start(va, f);
211	file_error_core(ms, error, f, va, 0);
212	va_end(va);
213}
214
215/*
216 * Print an error with magic line number.
217 */
218/*VARARGS*/
219protected void
220file_magerror(struct magic_set *ms, const char *f, ...)
221{
222	va_list va;
223	va_start(va, f);
224	file_error_core(ms, 0, f, va, ms->line);
225	va_end(va);
226}
227
228protected void
229file_oomem(struct magic_set *ms, size_t len)
230{
231	file_error(ms, errno, "cannot allocate %" SIZE_T_FORMAT "u bytes",
232	    len);
233}
234
235protected void
236file_badseek(struct magic_set *ms)
237{
238	file_error(ms, errno, "error seeking");
239}
240
241protected void
242file_badread(struct magic_set *ms)
243{
244	file_error(ms, errno, "error reading");
245}
246
247#ifndef COMPILE_ONLY
248
249protected int
250file_separator(struct magic_set *ms)
251{
252	return file_printf(ms, "\n- ");
253}
254
255static int
256checkdone(struct magic_set *ms, int *rv)
257{
258	if ((ms->flags & MAGIC_CONTINUE) == 0)
259		return 1;
260	if (file_separator(ms) == -1)
261		*rv = -1;
262	return 0;
263}
264
265protected int
266file_default(struct magic_set *ms, size_t nb)
267{
268	if (ms->flags & MAGIC_MIME) {
269		if ((ms->flags & MAGIC_MIME_TYPE) &&
270		    file_printf(ms, "application/%s",
271			nb ? "octet-stream" : "x-empty") == -1)
272			return -1;
273		return 1;
274	}
275	if (ms->flags & MAGIC_APPLE) {
276		if (file_printf(ms, "UNKNUNKN") == -1)
277			return -1;
278		return 1;
279	}
280	if (ms->flags & MAGIC_EXTENSION) {
281		if (file_printf(ms, "???") == -1)
282			return -1;
283		return 1;
284	}
285	return 0;
286}
287
288/*
289 * The magic detection functions return:
290 *	 1: found
291 *	 0: not found
292 *	-1: error
293 */
294/*ARGSUSED*/
295protected int
296file_buffer(struct magic_set *ms, int fd, struct stat *st,
297    const char *inname __attribute__ ((__unused__)),
298    const void *buf, size_t nb)
299{
300	int m = 0, rv = 0, looks_text = 0;
301	const char *code = NULL;
302	const char *code_mime = "binary";
303	const char *def = "data";
304	const char *ftype = NULL;
305	char *rbuf = NULL;
306	struct buffer b;
307
308	buffer_init(&b, fd, st, buf, nb);
309	ms->mode = b.st.st_mode;
310
311	if (nb == 0) {
312		def = "empty";
313		goto simple;
314	} else if (nb == 1) {
315		def = "very short file (no magic)";
316		goto simple;
317	}
318
319	if ((ms->flags & MAGIC_NO_CHECK_ENCODING) == 0) {
320		looks_text = file_encoding(ms, &b, NULL, 0,
321		    &code, &code_mime, &ftype);
322	}
323
324#ifdef __EMX__
325	if ((ms->flags & MAGIC_NO_CHECK_APPTYPE) == 0 && inname) {
326		m = file_os2_apptype(ms, inname, &b);
327		if ((ms->flags & MAGIC_DEBUG) != 0)
328			(void)fprintf(stderr, "[try os2_apptype %d]\n", m);
329		switch (m) {
330		case -1:
331			return -1;
332		case 0:
333			break;
334		default:
335			return 1;
336		}
337	}
338#endif
339#if HAVE_FORK
340	/* try compression stuff */
341	if ((ms->flags & MAGIC_NO_CHECK_COMPRESS) == 0) {
342		m = file_zmagic(ms, &b, inname);
343		if ((ms->flags & MAGIC_DEBUG) != 0)
344			(void)fprintf(stderr, "[try zmagic %d]\n", m);
345		if (m) {
346			goto done_encoding;
347		}
348	}
349#endif
350	/* Check if we have a tar file */
351	if ((ms->flags & MAGIC_NO_CHECK_TAR) == 0) {
352		m = file_is_tar(ms, &b);
353		if ((ms->flags & MAGIC_DEBUG) != 0)
354			(void)fprintf(stderr, "[try tar %d]\n", m);
355		if (m) {
356			if (checkdone(ms, &rv))
357				goto done;
358		}
359	}
360
361	/* Check if we have a JSON file */
362	if ((ms->flags & MAGIC_NO_CHECK_JSON) == 0) {
363		m = file_is_json(ms, &b);
364		if ((ms->flags & MAGIC_DEBUG) != 0)
365			(void)fprintf(stderr, "[try json %d]\n", m);
366		if (m) {
367			if (checkdone(ms, &rv))
368				goto done;
369		}
370	}
371
372	/* Check if we have a CSV file */
373	if ((ms->flags & MAGIC_NO_CHECK_CSV) == 0) {
374		m = file_is_csv(ms, &b, looks_text);
375		if ((ms->flags & MAGIC_DEBUG) != 0)
376			(void)fprintf(stderr, "[try csv %d]\n", m);
377		if (m) {
378			if (checkdone(ms, &rv))
379				goto done;
380		}
381	}
382
383	/* Check if we have a CDF file */
384	if ((ms->flags & MAGIC_NO_CHECK_CDF) == 0) {
385		m = file_trycdf(ms, &b);
386		if ((ms->flags & MAGIC_DEBUG) != 0)
387			(void)fprintf(stderr, "[try cdf %d]\n", m);
388		if (m) {
389			if (checkdone(ms, &rv))
390				goto done;
391		}
392	}
393#ifdef BUILTIN_ELF
394	if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && nb > 5 && fd != -1) {
395		file_pushbuf_t *pb;
396		/*
397		 * We matched something in the file, so this
398		 * *might* be an ELF file, and the file is at
399		 * least 5 bytes long, so if it's an ELF file
400		 * it has at least one byte past the ELF magic
401		 * number - try extracting information from the
402		 * ELF headers that cannot easily be  extracted
403		 * with rules in the magic file. We we don't
404		 * print the information yet.
405		 */
406		if ((pb = file_push_buffer(ms)) == NULL)
407			return -1;
408
409		rv = file_tryelf(ms, &b);
410		rbuf = file_pop_buffer(ms, pb);
411		if (rv == -1) {
412			free(rbuf);
413			rbuf = NULL;
414		}
415		if ((ms->flags & MAGIC_DEBUG) != 0)
416			(void)fprintf(stderr, "[try elf %d]\n", m);
417	}
418#endif
419
420	/* try soft magic tests */
421	if ((ms->flags & MAGIC_NO_CHECK_SOFT) == 0) {
422		m = file_softmagic(ms, &b, NULL, NULL, BINTEST, looks_text);
423		if ((ms->flags & MAGIC_DEBUG) != 0)
424			(void)fprintf(stderr, "[try softmagic %d]\n", m);
425		if (m == 1 && rbuf) {
426			if (file_printf(ms, "%s", rbuf) == -1)
427				goto done;
428		}
429		if (m) {
430			if (checkdone(ms, &rv))
431				goto done;
432		}
433	}
434
435	/* try text properties */
436	if ((ms->flags & MAGIC_NO_CHECK_TEXT) == 0) {
437
438		m = file_ascmagic(ms, &b, looks_text);
439		if ((ms->flags & MAGIC_DEBUG) != 0)
440			(void)fprintf(stderr, "[try ascmagic %d]\n", m);
441		if (m) {
442			goto done;
443		}
444	}
445
446simple:
447	/* give up */
448	if (m == 0) {
449		m = 1;
450		rv = file_default(ms, nb);
451		if (rv == 0)
452			if (file_printf(ms, "%s", def) == -1)
453				rv = -1;
454	}
455 done:
456	if ((ms->flags & MAGIC_MIME_ENCODING) != 0) {
457		if (ms->flags & MAGIC_MIME_TYPE)
458			if (file_printf(ms, "; charset=") == -1)
459				rv = -1;
460		if (file_printf(ms, "%s", code_mime) == -1)
461			rv = -1;
462	}
463#if HAVE_FORK
464 done_encoding:
465#endif
466	free(rbuf);
467	buffer_fini(&b);
468	if (rv)
469		return rv;
470
471	return m;
472}
473#endif
474
475protected int
476file_reset(struct magic_set *ms, int checkloaded)
477{
478	if (checkloaded && ms->mlist[0] == NULL) {
479		file_error(ms, 0, "no magic files loaded");
480		return -1;
481	}
482	file_clearbuf(ms);
483	if (ms->o.pbuf) {
484		free(ms->o.pbuf);
485		ms->o.pbuf = NULL;
486	}
487	ms->event_flags &= ~EVENT_HAD_ERR;
488	ms->error = -1;
489	return 0;
490}
491
492#define OCTALIFY(n, o)	\
493	/*LINTED*/ \
494	(void)(*(n)++ = '\\', \
495	*(n)++ = ((CAST(uint32_t, *(o)) >> 6) & 3) + '0', \
496	*(n)++ = ((CAST(uint32_t, *(o)) >> 3) & 7) + '0', \
497	*(n)++ = ((CAST(uint32_t, *(o)) >> 0) & 7) + '0', \
498	(o)++)
499
500protected const char *
501file_getbuffer(struct magic_set *ms)
502{
503	char *pbuf, *op, *np;
504	size_t psize, len;
505
506	if (ms->event_flags & EVENT_HAD_ERR)
507		return NULL;
508
509	if (ms->flags & MAGIC_RAW)
510		return ms->o.buf;
511
512	if (ms->o.buf == NULL)
513		return NULL;
514
515	/* * 4 is for octal representation, + 1 is for NUL */
516	len = strlen(ms->o.buf);
517	if (len > (SIZE_MAX - 1) / 4) {
518		file_oomem(ms, len);
519		return NULL;
520	}
521	psize = len * 4 + 1;
522	if ((pbuf = CAST(char *, realloc(ms->o.pbuf, psize))) == NULL) {
523		file_oomem(ms, psize);
524		return NULL;
525	}
526	ms->o.pbuf = pbuf;
527
528#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
529	{
530		mbstate_t state;
531		wchar_t nextchar;
532		int mb_conv = 1;
533		size_t bytesconsumed;
534		char *eop;
535		(void)memset(&state, 0, sizeof(mbstate_t));
536
537		np = ms->o.pbuf;
538		op = ms->o.buf;
539		eop = op + len;
540
541		while (op < eop) {
542			bytesconsumed = mbrtowc(&nextchar, op,
543			    CAST(size_t, eop - op), &state);
544			if (bytesconsumed == CAST(size_t, -1) ||
545			    bytesconsumed == CAST(size_t, -2)) {
546				mb_conv = 0;
547				break;
548			}
549
550			if (iswprint(nextchar)) {
551				(void)memcpy(np, op, bytesconsumed);
552				op += bytesconsumed;
553				np += bytesconsumed;
554			} else {
555				while (bytesconsumed-- > 0)
556					OCTALIFY(np, op);
557			}
558		}
559		*np = '\0';
560
561		/* Parsing succeeded as a multi-byte sequence */
562		if (mb_conv != 0)
563			return ms->o.pbuf;
564	}
565#endif
566
567	for (np = ms->o.pbuf, op = ms->o.buf; *op;) {
568		if (isprint(CAST(unsigned char, *op))) {
569			*np++ = *op++;
570		} else {
571			OCTALIFY(np, op);
572		}
573	}
574	*np = '\0';
575	return ms->o.pbuf;
576}
577
578protected int
579file_check_mem(struct magic_set *ms, unsigned int level)
580{
581	size_t len;
582
583	if (level >= ms->c.len) {
584		len = (ms->c.len = 20 + level) * sizeof(*ms->c.li);
585		ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ?
586		    malloc(len) :
587		    realloc(ms->c.li, len));
588		if (ms->c.li == NULL) {
589			file_oomem(ms, len);
590			return -1;
591		}
592	}
593	ms->c.li[level].got_match = 0;
594#ifdef ENABLE_CONDITIONALS
595	ms->c.li[level].last_match = 0;
596	ms->c.li[level].last_cond = COND_NONE;
597#endif /* ENABLE_CONDITIONALS */
598	return 0;
599}
600
601protected size_t
602file_printedlen(const struct magic_set *ms)
603{
604	return ms->o.blen;
605}
606
607protected int
608file_replace(struct magic_set *ms, const char *pat, const char *rep)
609{
610	file_regex_t rx;
611	int rc, rv = -1;
612
613	rc = file_regcomp(&rx, pat, REG_EXTENDED);
614	if (rc) {
615		file_regerror(&rx, rc, ms);
616	} else {
617		regmatch_t rm;
618		int nm = 0;
619		while (file_regexec(&rx, ms->o.buf, 1, &rm, 0) == 0) {
620			ms->o.buf[rm.rm_so] = '\0';
621			if (file_printf(ms, "%s%s", rep,
622			    rm.rm_eo != 0 ? ms->o.buf + rm.rm_eo : "") == -1)
623				goto out;
624			nm++;
625		}
626		rv = nm;
627	}
628out:
629	file_regfree(&rx);
630	return rv;
631}
632
633protected int
634file_regcomp(file_regex_t *rx, const char *pat, int flags)
635{
636#ifdef USE_C_LOCALE
637	rx->c_lc_ctype = newlocale(LC_CTYPE_MASK, "C", 0);
638	assert(rx->c_lc_ctype != NULL);
639	rx->old_lc_ctype = uselocale(rx->c_lc_ctype);
640	assert(rx->old_lc_ctype != NULL);
641#else
642	rx->old_lc_ctype = setlocale(LC_CTYPE, NULL);
643	assert(rx->old_lc_ctype != NULL);
644	rx->old_lc_ctype = strdup(rx->old_lc_ctype);
645	assert(rx->old_lc_ctype != NULL);
646	(void)setlocale(LC_CTYPE, "C");
647#endif
648	rx->pat = pat;
649
650	return rx->rc = regcomp(&rx->rx, pat, flags);
651}
652
653protected int
654file_regexec(file_regex_t *rx, const char *str, size_t nmatch,
655    regmatch_t* pmatch, int eflags)
656{
657	assert(rx->rc == 0);
658	/* XXX: force initialization because glibc does not always do this */
659	if (nmatch != 0)
660		memset(pmatch, 0, nmatch * sizeof(*pmatch));
661	return regexec(&rx->rx, str, nmatch, pmatch, eflags);
662}
663
664protected void
665file_regfree(file_regex_t *rx)
666{
667	if (rx->rc == 0)
668		regfree(&rx->rx);
669#ifdef USE_C_LOCALE
670	(void)uselocale(rx->old_lc_ctype);
671	freelocale(rx->c_lc_ctype);
672#else
673	(void)setlocale(LC_CTYPE, rx->old_lc_ctype);
674	free(rx->old_lc_ctype);
675#endif
676}
677
678protected void
679file_regerror(file_regex_t *rx, int rc, struct magic_set *ms)
680{
681	char errmsg[512];
682
683	(void)regerror(rc, &rx->rx, errmsg, sizeof(errmsg));
684	file_magerror(ms, "regex error %d for `%s', (%s)", rc, rx->pat,
685	    errmsg);
686}
687
688protected file_pushbuf_t *
689file_push_buffer(struct magic_set *ms)
690{
691	file_pushbuf_t *pb;
692
693	if (ms->event_flags & EVENT_HAD_ERR)
694		return NULL;
695
696	if ((pb = (CAST(file_pushbuf_t *, malloc(sizeof(*pb))))) == NULL)
697		return NULL;
698
699	pb->buf = ms->o.buf;
700	pb->blen = ms->o.blen;
701	pb->offset = ms->offset;
702
703	ms->o.buf = NULL;
704	ms->o.blen = 0;
705	ms->offset = 0;
706
707	return pb;
708}
709
710protected char *
711file_pop_buffer(struct magic_set *ms, file_pushbuf_t *pb)
712{
713	char *rbuf;
714
715	if (ms->event_flags & EVENT_HAD_ERR) {
716		free(pb->buf);
717		free(pb);
718		return NULL;
719	}
720
721	rbuf = ms->o.buf;
722
723	ms->o.buf = pb->buf;
724	ms->o.blen = pb->blen;
725	ms->offset = pb->offset;
726
727	free(pb);
728	return rbuf;
729}
730
731/*
732 * convert string to ascii printable format.
733 */
734protected char *
735file_printable(char *buf, size_t bufsiz, const char *str, size_t slen)
736{
737	char *ptr, *eptr = buf + bufsiz - 1;
738	const unsigned char *s = RCAST(const unsigned char *, str);
739	const unsigned char *es = s + slen;
740
741	for (ptr = buf;  ptr < eptr && s < es && *s; s++) {
742		if (isprint(*s)) {
743			*ptr++ = *s;
744			continue;
745		}
746		if (ptr >= eptr - 3)
747			break;
748		*ptr++ = '\\';
749		*ptr++ = ((CAST(unsigned int, *s) >> 6) & 7) + '0';
750		*ptr++ = ((CAST(unsigned int, *s) >> 3) & 7) + '0';
751		*ptr++ = ((CAST(unsigned int, *s) >> 0) & 7) + '0';
752	}
753	*ptr = '\0';
754	return buf;
755}
756
757struct guid {
758	uint32_t data1;
759	uint16_t data2;
760	uint16_t data3;
761	uint8_t data4[8];
762};
763
764protected int
765file_parse_guid(const char *s, uint64_t *guid)
766{
767	struct guid *g = CAST(struct guid *, guid);
768	return sscanf(s,
769	    "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
770	    &g->data1, &g->data2, &g->data3, &g->data4[0], &g->data4[1],
771	    &g->data4[2], &g->data4[3], &g->data4[4], &g->data4[5],
772	    &g->data4[6], &g->data4[7]) == 11 ? 0 : -1;
773}
774
775protected int
776file_print_guid(char *str, size_t len, const uint64_t *guid)
777{
778	const struct guid *g = CAST(const struct guid *, guid);
779
780	return snprintf(str, len, "%.8X-%.4hX-%.4hX-%.2hhX%.2hhX-"
781	    "%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX",
782	    g->data1, g->data2, g->data3, g->data4[0], g->data4[1],
783	    g->data4[2], g->data4[3], g->data4[4], g->data4[5],
784	    g->data4[6], g->data4[7]);
785}
786