xref: /aosp_15_r20/external/erofs-utils/lib/uuid.c (revision 33b1fccf6a0fada2c2875d400ed01119b7676ee5)
1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 /*
3  * Copyright (C) 2023 Norbert Lange <[email protected]>
4  */
5 
6 #include <string.h>
7 #include <errno.h>
8 
9 #include "erofs/config.h"
10 #include "erofs/defs.h"
11 #include "liberofs_uuid.h"
12 
13 #ifdef HAVE_LIBUUID
14 #include <uuid.h>
15 #else
16 
17 #include <stdlib.h>
18 #ifdef HAVE_SYS_RANDOM_H
19 #include <sys/random.h>
20 #else
21 #define _GNU_SOURCE
22 #include <unistd.h>
23 #include <sys/syscall.h>
24 #endif
25 
26 /* Flags to be used, will be modified if kernel does not support them */
27 static unsigned int erofs_grnd_flag =
28 #ifdef GRND_INSECURE
29 	GRND_INSECURE;
30 #else
31 	0x0004;
32 #endif
33 
s_getrandom(void * out,unsigned size,bool insecure)34 static int s_getrandom(void *out, unsigned size, bool insecure)
35 {
36 	unsigned int kflags = erofs_grnd_flag;
37 	unsigned int flags = insecure ? kflags : 0;
38 
39 	for (;;)
40 	{
41 		ssize_t r;
42 		int err;
43 
44 #ifdef HAVE_SYS_RANDOM_H
45 		r = getrandom(out, size, flags);
46 #elif defined(__NR_getrandom)
47 		r = (ssize_t)syscall(__NR_getrandom, out, size, flags);
48 #else
49 		r = -1;
50 		errno = ENOSYS;
51 		(void)flags;
52 #endif
53 
54 		if (r == size)
55 			break;
56 		err = errno;
57 		if (err != EINTR) {
58 			if (__erofs_unlikely(err == ENOSYS && insecure)) {
59 				while (size) {
60 					*(u8 *)out++ = rand() % 256;
61 					--size;
62 				}
63 				err = 0;
64 			} else if (err == EINVAL && kflags) {
65 				// Kernel likely does not support GRND_INSECURE
66 				erofs_grnd_flag = 0;
67 				kflags = 0;
68 				continue;
69 			}
70 			return -err;
71 		}
72 	}
73 	return 0;
74 }
75 #endif
76 
erofs_uuid_generate(unsigned char * out)77 void erofs_uuid_generate(unsigned char *out)
78 {
79 #ifdef HAVE_LIBUUID
80 	uuid_t new_uuid;
81 
82 	do {
83 		uuid_generate(new_uuid);
84 	} while (uuid_is_null(new_uuid));
85 #else
86 	unsigned char new_uuid[16];
87 	int res __maybe_unused;
88 
89 	res = s_getrandom(new_uuid, sizeof(new_uuid), true);
90 	BUG_ON(res != 0);
91 
92 	// UID type + version bits
93 	new_uuid[0] = (new_uuid[4 + 2] & 0x0f) | 0x40;
94 	new_uuid[1] = (new_uuid[4 + 2 + 2] & 0x3f) | 0x80;
95 #endif
96 	memcpy(out, new_uuid, sizeof(new_uuid));
97 }
98 
erofs_uuid_parse(const char * in,unsigned char * uu)99 int erofs_uuid_parse(const char *in, unsigned char *uu) {
100 #ifdef HAVE_LIBUUID
101 	return uuid_parse((char *)in, uu);
102 #else
103 	unsigned char new_uuid[16];
104 	unsigned int hypens = ((1U << 3) | (1U << 5) | (1U << 7) | (1U << 9));
105 	int i;
106 
107 	for (i = 0; i < sizeof(new_uuid); hypens >>= 1, i++)
108 	{
109 		char c[] = { in[0], in[1], '\0' };
110 		char* endptr = c;
111 		unsigned long val = strtoul(c, &endptr, 16);
112 
113 		if (endptr - c != 2)
114 			return -EINVAL;
115 
116 		in += 2;
117 
118 		if ((hypens & 1U) != 0) {
119 			if (*in++ != '-')
120 				return -EINVAL;
121 		}
122 		new_uuid[i] = (unsigned char)val;
123 	}
124 
125 	if (*in != '\0')
126 		return -EINVAL;
127 	memcpy(uu, new_uuid, sizeof(new_uuid));
128 	return 0;
129 #endif
130 }
131