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